From aa137f7d4adb8ebbbdd3a4c71cd28824d6fcfaca Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 10 Mar 2025 22:56:34 +0100 Subject: check_curl: clang-format --- plugins/check_curl.c | 429 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 271 insertions(+), 158 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 748201e8..cf755316 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -32,8 +32,8 @@ * * *****************************************************************************/ -const char *progname = "check_curl"; +const char *progname = "check_curl"; const char *copyright = "2006-2024"; const char *email = "devel@monitoring-plugins.org"; @@ -63,7 +63,7 @@ const char *email = "devel@monitoring-plugins.org"; #include -#define MAKE_LIBCURL_VERSION(major, minor, patch) ((major)*0x10000 + (minor)*0x100 + (patch)) +#define MAKE_LIBCURL_VERSION(major, minor, patch) ((major) * 0x10000 + (minor) * 0x100 + (patch)) #define DEFAULT_BUFFER_SIZE 2048 #define DEFAULT_SERVER_URL "/" @@ -275,12 +275,14 @@ int main(int argc, char **argv) { snprintf(user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", progname, NP_VERSION, VERSION, curl_version()); /* parse arguments */ - if (process_arguments(argc, argv) == false) + if (process_arguments(argc, argv) == false) { usage4(_("Could not parse arguments")); + } - if (display_html) + if (display_html) { printf("", use_ssl ? "https" : "http", host_name ? host_name : server_address, virtual_port ? virtual_port : server_port, server_url); + } result = check_http(); return result; @@ -361,14 +363,16 @@ static int expected_statuscode(const char *reply, const char *statuscodes) { char *code; int result = 0; - if ((expected = strdup(statuscodes)) == NULL) + if ((expected = strdup(statuscodes)) == NULL) { die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); + } - for (code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) + for (code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) { if (strstr(reply, code) != NULL) { result = 1; break; } + } free(expected); return result; @@ -396,8 +400,9 @@ int lookup_host(const char *host, char *buf, size_t buflen) { hints.ai_flags |= AI_CANONNAME; errcode = getaddrinfo(host, NULL, &hints, &result); - if (errcode != 0) + if (errcode != 0) { return errcode; + } strcpy(buf, ""); res = result; @@ -437,23 +442,29 @@ int lookup_host(const char *host, char *buf, size_t buflen) { } static void cleanup(void) { - if (status_line_initialized) + if (status_line_initialized) { curlhelp_free_statusline(&status_line); + } status_line_initialized = false; - if (curl_easy_initialized) + if (curl_easy_initialized) { curl_easy_cleanup(curl); + } curl_easy_initialized = false; - if (curl_global_initialized) + if (curl_global_initialized) { curl_global_cleanup(); + } curl_global_initialized = false; - if (body_buf_initialized) + if (body_buf_initialized) { curlhelp_freewritebuffer(&body_buf); + } body_buf_initialized = false; - if (header_buf_initialized) + if (header_buf_initialized) { curlhelp_freewritebuffer(&header_buf); + } header_buf_initialized = false; - if (put_buf_initialized) + if (put_buf_initialized) { curlhelp_freereadbuffer(&put_buf); + } put_buf_initialized = false; } @@ -468,8 +479,9 @@ int check_http(void) { char dnscache[DEFAULT_BUFFER_SIZE]; /* initialize curl */ - if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) + if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n"); + } curl_global_initialized = true; if ((curl = curl_easy_init()) == NULL) { @@ -480,8 +492,9 @@ int check_http(void) { /* register cleanup function to shut down libcurl properly */ atexit(cleanup); - if (verbose >= 1) + if (verbose >= 1) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_VERBOSE, 1), "CURLOPT_VERBOSE"); + } /* print everything on stdout like check_http would do */ handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_STDERR, stdout), "CURLOPT_STDERR"); @@ -494,16 +507,18 @@ int check_http(void) { #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */ /* initialize buffer for body of the answer */ - if (curlhelp_initwritebuffer(&body_buf) < 0) + if (curlhelp_initwritebuffer(&body_buf) < 0) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n"); + } body_buf_initialized = true; handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback), "CURLOPT_WRITEFUNCTION"); handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&body_buf), "CURLOPT_WRITEDATA"); /* initialize buffer for header of the answer */ - if (curlhelp_initwritebuffer(&header_buf) < 0) + if (curlhelp_initwritebuffer(&header_buf) < 0) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n"); + } header_buf_initialized = true; handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback), "CURLOPT_HEADERFUNCTION"); @@ -532,8 +547,9 @@ int check_http(void) { snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", host_name, server_port, addrstr); host = curl_slist_append(NULL, dnscache); curl_easy_setopt(curl, CURLOPT_RESOLVE, host); - if (verbose >= 1) + if (verbose >= 1) { printf("* curl CURLOPT_RESOLVE: %s\n", dnscache); + } } // If server_address is an IPv6 address it must be surround by square brackets @@ -552,16 +568,18 @@ int check_http(void) { snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", use_ssl ? "https" : "http", (use_ssl & (host_name != NULL)) ? host_name : server_address, server_port, server_url); - if (verbose >= 1) + if (verbose >= 1) { printf("* curl CURLOPT_URL: %s\n", url); + } handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, url), "CURLOPT_URL"); /* extract proxy information for legacy proxy https requests */ if (!strcmp(http_method, "CONNECT") || strstr(server_url, "http") == server_url) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXY, server_address), "CURLOPT_PROXY"); handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYPORT, (long)server_port), "CURLOPT_PROXYPORT"); - if (verbose >= 2) + if (verbose >= 2) { printf("* curl CURLOPT_PROXY: %s:%d\n", server_address, server_port); + } http_method = "GET"; handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, server_url), "CURLOPT_URL"); } @@ -576,12 +594,13 @@ int check_http(void) { /* set HTTP method */ if (http_method) { - if (!strcmp(http_method, "POST")) + if (!strcmp(http_method, "POST")) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POST, 1), "CURLOPT_POST"); - else if (!strcmp(http_method, "PUT")) + } else if (!strcmp(http_method, "PUT")) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD"); - else + } else { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, http_method), "CURLOPT_CUSTOMREQUEST"); + } } /* check if Host header is explicitly set in options */ @@ -629,10 +648,12 @@ int check_http(void) { } /* client certificate and key to present to server (SSL) */ - if (client_cert) + if (client_cert) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLCERT, client_cert), "CURLOPT_SSLCERT"); - if (client_privkey) + } + if (client_privkey) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLKEY, client_privkey), "CURLOPT_SSLKEY"); + } if (ca_cert) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CAINFO, ca_cert), "CURLOPT_CAINFO"); } @@ -700,18 +721,20 @@ int check_http(void) { } # else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ /* old libcurl, our only hope is OpenSSL, otherwise we are out of luck */ - if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL || ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL) + if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL || ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL) { add_sslctx_verify_fun = true; - else + } else { die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl " "too old and has no CURLOPT_CERTINFO)\n"); + } # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ } # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 10, 6) /* required for CURLOPT_SSL_CTX_FUNCTION */ // ssl ctx function is not available with all ssl backends - if (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, NULL) != CURLE_UNKNOWN_OPTION) + if (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, NULL) != CURLE_UNKNOWN_OPTION) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION"); + } # endif #endif /* LIBCURL_FEATURE_SSL */ @@ -720,12 +743,14 @@ int check_http(void) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent), "CURLOPT_USERAGENT"); /* proxy-authentication */ - if (strcmp(proxy_auth, "")) + if (strcmp(proxy_auth, "")) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy_auth), "CURLOPT_PROXYUSERPWD"); + } /* authentication */ - if (strcmp(user_auth, "")) + if (strcmp(user_auth, "")) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERPWD, user_auth), "CURLOPT_USERPWD"); + } /* TODO: parameter auth method, bitfield of following methods: * CURLAUTH_BASIC (default) @@ -774,20 +799,23 @@ int check_http(void) { } /* no-body */ - if (no_body) + if (no_body) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_NOBODY, 1), "CURLOPT_NOBODY"); + } /* IPv4 or IPv6 forced DNS resolution */ - if (address_family == AF_UNSPEC) + if (address_family == AF_UNSPEC) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)"); - else if (address_family == AF_INET) + } else if (address_family == AF_INET) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)"); + } #if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) - else if (address_family == AF_INET6) + else if (address_family == AF_INET6) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)"); + } #endif /* either send http POST data (any data, not only POST)*/ @@ -799,16 +827,18 @@ int check_http(void) { } /* NULL indicates "HTTP Continue" in libcurl, provide an empty string * in case of no POST/PUT data */ - if (!http_post_data) + if (!http_post_data) { http_post_data = ""; + } if (!strcmp(http_method, "POST")) { /* POST method, set payload with CURLOPT_POSTFIELDS */ handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POSTFIELDS, http_post_data), "CURLOPT_POSTFIELDS"); } else if (!strcmp(http_method, "PUT")) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_READFUNCTION, (curl_read_callback)curlhelp_buffer_read_callback), "CURLOPT_READFUNCTION"); - if (curlhelp_initreadbuffer(&put_buf, http_post_data, strlen(http_post_data)) < 0) + if (curlhelp_initreadbuffer(&put_buf, http_post_data, strlen(http_post_data)) < 0) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating read buffer for PUT\n"); + } put_buf_initialized = true; handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_READDATA, (void *)&put_buf), "CURLOPT_READDATA"); handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_INFILESIZE, (curl_off_t)strlen(http_post_data)), @@ -821,15 +851,17 @@ int check_http(void) { /* enable reading cookies from a file, and if the filename is an empty string, only enable the curl cookie engine */ handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookie_jar_file), "CURLOPT_COOKIEFILE"); /* now enable saving cookies to a file, but only if the filename is not an empty string, since writing it would fail */ - if (*cookie_jar_file) + if (*cookie_jar_file) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookie_jar_file), "CURLOPT_COOKIEJAR"); + } } /* do the request */ res = curl_easy_perform(curl); - if (verbose >= 2 && http_post_data) + if (verbose >= 2 && http_post_data) { printf("**** REQUEST CONTENT ****\n%s\n", http_post_data); + } /* free header and server IP resolve lists, we don't need it anymore */ curl_slist_free_all(header_list); @@ -879,8 +911,9 @@ int check_http(void) { const char *raw_cert = NULL; for (i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) { for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) { - if (verbose >= 2) + if (verbose >= 2) { printf("%d ** %s\n", i, slist->data); + } if (strncmp(slist->data, "Cert:", 5) == 0) { raw_cert = &slist->data[5]; goto GOT_FIRST_CERT; @@ -944,8 +977,9 @@ int check_http(void) { } /* return a CRITICAL status if we couldn't read any data */ - if (strlen(header_buf.buf) == 0 && strlen(body_buf.buf) == 0) + if (strlen(header_buf.buf) == 0 && strlen(body_buf.buf) == 0) { die(STATE_CRITICAL, _("HTTP CRITICAL - No header received from host\n")); + } /* get status line of answer, check sanity of HTTP code */ if (curlhelp_parse_statusline(header_buf.buf, &status_line) < 0) { @@ -957,8 +991,9 @@ int check_http(void) { /* get result code from cURL */ handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code), "CURLINFO_RESPONSE_CODE"); - if (verbose >= 2) + if (verbose >= 2) { printf("* curl CURLINFO_RESPONSE_CODE is %ld\n", code); + } /* print status line, header, body if verbose */ if (verbose >= 2) { @@ -967,18 +1002,20 @@ int check_http(void) { /* make sure the status line matches the response we are looking for */ if (!expected_statuscode(status_line.first_line, server_expect)) { - if (server_port == HTTP_PORT) + if (server_port == HTTP_PORT) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), status_line.first_line); - else + } else { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), server_port, status_line.first_line); + } die(STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg, show_body ? "\n" : "", show_body ? body_buf.buf : ""); } if (server_expect_yn) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), server_expect); - if (verbose) + if (verbose) { printf("%s\n", msg); + } result = STATE_OK; } else { /* illegal return codes result in a critical state */ @@ -1017,8 +1054,9 @@ int check_http(void) { /* libcurl redirection internally, handle error states here */ if (followmethod == FOLLOW_LIBCURL) { handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &redir_depth), "CURLINFO_REDIRECT_COUNT"); - if (verbose >= 2) + if (verbose >= 2) { printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth); + } if (redir_depth > max_depth) { snprintf(msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl", max_depth); die(STATE_WARNING, "HTTP WARNING - %s", msg); @@ -1130,10 +1168,11 @@ int check_http(void) { /* Cut-off trailing characters */ if (strlen(msg) >= 2) { - if (msg[strlen(msg) - 2] == ',') + if (msg[strlen(msg) - 2] == ',') { msg[strlen(msg) - 2] = '\0'; - else + } else { msg[strlen(msg) - 3] = '\0'; + } } /* TODO: separate _() msg and status code: die (result, "HTTP %s: %s\n", state_text(result), msg); */ @@ -1146,16 +1185,19 @@ int check_http(void) { } int uri_strcmp(const UriTextRangeA range, const char *s) { - if (!range.first) + if (!range.first) { return -1; - if ((size_t)(range.afterLast - range.first) < strlen(s)) + } + if ((size_t)(range.afterLast - range.first) < strlen(s)) { return -1; + } return strncmp(s, range.first, min((size_t)(range.afterLast - range.first), strlen(s))); } char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) { - if (!range.first) + if (!range.first) { return "(null)"; + } strncpy(buf, range.first, max(buflen - 1, (size_t)(range.afterLast - range.first))); buf[max(buflen - 1, (size_t)(range.afterLast - range.first))] = '\0'; buf[range.afterLast - range.first] = '\0'; @@ -1183,12 +1225,14 @@ void redir(curlhelp_write_curlbuf *header_buf) { location = get_header_value(headers, nof_headers, "location"); - if (verbose >= 2) + if (verbose >= 2) { printf(_("* Seen redirect location %s\n"), location); + } - if (++redir_depth > max_depth) + if (++redir_depth > max_depth) { die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s%s\n"), max_depth, location, (display_html ? "" : "")); + } UriParserStateA state; UriUriA uri; @@ -1230,10 +1274,11 @@ void redir(curlhelp_write_curlbuf *header_buf) { } if (uri.scheme.first) { - if (!uri_strcmp(uri.scheme, "https")) + if (!uri_strcmp(uri.scheme, "https")) { use_ssl = true; - else + } else { use_ssl = false; + } } /* we do a sloppy test here only, because uriparser would have failed @@ -1243,11 +1288,13 @@ void redir(curlhelp_write_curlbuf *header_buf) { new_port = atoi(uri_string(uri.portText, buf, DEFAULT_BUFFER_SIZE)); } else { new_port = HTTP_PORT; - if (use_ssl) + if (use_ssl) { new_port = HTTPS_PORT; + } } - if (new_port > MAX_PORT) + if (new_port > MAX_PORT) { die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s%s\n"), MAX_PORT, location, display_html ? "" : ""); + } /* by RFC 7231 relative URLs in Location should be taken relative to * the original URL, so we try to form a new absolute URL here @@ -1255,8 +1302,9 @@ void redir(curlhelp_write_curlbuf *header_buf) { if (!uri.scheme.first && !uri.hostText.first) { new_host = strdup(host_name ? host_name : server_address); new_port = server_port; - if (use_ssl) + if (use_ssl) { uri_string(uri.scheme, "https", DEFAULT_BUFFER_SIZE); + } } else { new_host = strdup(uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE)); } @@ -1273,9 +1321,10 @@ void redir(curlhelp_write_curlbuf *header_buf) { } if (server_port == new_port && !strncmp(server_address, new_host, MAX_IPV4_HOSTLENGTH) && - (host_name && !strncmp(host_name, new_host, MAX_IPV4_HOSTLENGTH)) && !strcmp(server_url, new_url)) + (host_name && !strncmp(host_name, new_host, MAX_IPV4_HOSTLENGTH)) && !strcmp(server_url, new_url)) { die(STATE_CRITICAL, _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), use_ssl ? "https" : "http", new_host, new_port, new_url, (display_html ? "" : "")); + } /* set new values for redirected request */ @@ -1299,9 +1348,10 @@ void redir(curlhelp_write_curlbuf *header_buf) { uriFreeUriMembersA(&uri); - if (verbose) + if (verbose) { printf(_("Redirection to %s://%s:%d%s\n"), use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url); + } /* TODO: the hash component MUST be taken from the original URL and * attached to the URL in Location @@ -1313,8 +1363,9 @@ void redir(curlhelp_write_curlbuf *header_buf) { /* check whether a file exists */ void test_file(char *path) { - if (access(path, R_OK) == 0) + if (access(path, R_OK) == 0) { return; + } usage2(_("file does not exist or is not readable"), path); } @@ -1383,29 +1434,36 @@ bool process_arguments(int argc, char **argv) { {"haproxy-protocol", no_argument, 0, HAPROXY_PROTOCOL}, {0, 0, 0, 0}}; - if (argc < 2) + if (argc < 2) { return false; + } /* support check_http compatible arguments */ for (c = 1; c < argc; c++) { - if (strcmp("-to", argv[c]) == 0) + if (strcmp("-to", argv[c]) == 0) { strcpy(argv[c], "-t"); - if (strcmp("-hn", argv[c]) == 0) + } + if (strcmp("-hn", argv[c]) == 0) { strcpy(argv[c], "-H"); - if (strcmp("-wt", argv[c]) == 0) + } + if (strcmp("-wt", argv[c]) == 0) { strcpy(argv[c], "-w"); - if (strcmp("-ct", argv[c]) == 0) + } + if (strcmp("-ct", argv[c]) == 0) { strcpy(argv[c], "-c"); - if (strcmp("-nohtml", argv[c]) == 0) + } + if (strcmp("-nohtml", argv[c]) == 0) { strcpy(argv[c], "-n"); + } } server_url = strdup(DEFAULT_SERVER_URL); while (1) { c = getopt_long(argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:DnlLS::m:M:NEB", longopts, &option); - if (c == -1 || c == EOF || c == 1) + if (c == -1 || c == EOF || c == 1) { break; + } switch (c) { case 'h': @@ -1421,10 +1479,11 @@ bool process_arguments(int argc, char **argv) { verbose++; break; case 't': /* timeout period */ - if (!is_intnonneg(optarg)) + if (!is_intnonneg(optarg)) { usage2(_("Timeout interval must be a positive integer"), optarg); - else + } else { socket_timeout = (int)strtol(optarg, NULL, 10); + } break; case 'c': /* critical time threshold */ critical_thresholds = optarg; @@ -1457,11 +1516,12 @@ bool process_arguments(int argc, char **argv) { server_url = strdup(optarg); break; case 'p': /* Server port */ - if (!is_intnonneg(optarg)) + if (!is_intnonneg(optarg)) { usage2(_("Invalid port number, expecting a non-negative number"), optarg); - else { - if (strtol(optarg, NULL, 10) > MAX_PORT) + } else { + if (strtol(optarg, NULL, 10) > MAX_PORT) { usage2(_("Invalid port number, supplied port number is too big"), optarg); + } server_port = (unsigned short)strtol(optarg, NULL, 10); specify_port = true; } @@ -1475,14 +1535,17 @@ bool process_arguments(int argc, char **argv) { proxy_auth[MAX_INPUT_BUFFER - 1] = 0; break; case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */ - if (!http_post_data) + if (!http_post_data) { http_post_data = strdup(optarg); - if (!http_method) + } + if (!http_method) { http_method = strdup("POST"); + } break; case 'j': /* Set HTTP method */ - if (http_method) + if (http_method) { free(http_method); + } http_method = strdup(optarg); break; case 'A': /* useragent */ @@ -1490,10 +1553,11 @@ bool process_arguments(int argc, char **argv) { user_agent[DEFAULT_BUFFER_SIZE - 1] = '\0'; break; case 'k': /* Additional headers */ - if (http_opt_headers_count == 0) + if (http_opt_headers_count == 0) { http_opt_headers = malloc(sizeof(char *) * (++http_opt_headers_count)); - else + } else { http_opt_headers = realloc(http_opt_headers, sizeof(char *) * (++http_opt_headers_count)); + } http_opt_headers[http_opt_headers_count - 1] = optarg; break; case 'L': /* show html link */ @@ -1506,18 +1570,21 @@ bool process_arguments(int argc, char **argv) { #ifdef LIBCURL_FEATURE_SSL if ((temp = strchr(optarg, ',')) != NULL) { *temp = '\0'; - if (!is_intnonneg(optarg)) + if (!is_intnonneg(optarg)) { usage2(_("Invalid certificate expiration period"), optarg); + } days_till_exp_warn = atoi(optarg); *temp = ','; temp++; - if (!is_intnonneg(temp)) + if (!is_intnonneg(temp)) { usage2(_("Invalid certificate expiration period"), temp); + } days_till_exp_crit = atoi(temp); } else { days_till_exp_crit = 0; - if (!is_intnonneg(optarg)) + if (!is_intnonneg(optarg)) { usage2(_("Invalid certificate expiration period"), optarg); + } days_till_exp_warn = atoi(optarg); } check_cert = true; @@ -1566,11 +1633,11 @@ bool process_arguments(int argc, char **argv) { *plus_ptr = '\0'; } - if (optarg[0] == '2') + if (optarg[0] == '2') { ssl_version = CURL_SSLVERSION_SSLv2; - else if (optarg[0] == '3') + } else if (optarg[0] == '3') { ssl_version = CURL_SSLVERSION_SSLv3; - else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0")) + } else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0")) # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) ssl_version = CURL_SSLVERSION_TLSv1_0; # else @@ -1594,8 +1661,9 @@ bool process_arguments(int argc, char **argv) { # else ssl_version = CURL_SSLVERSION_DEFAULT; # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */ - else + else { usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 (with optional '+' suffix)")); + } } # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) if (got_plus) { @@ -1626,10 +1694,12 @@ bool process_arguments(int argc, char **argv) { } } # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */ - if (verbose >= 2) + if (verbose >= 2) { printf(_("* Set SSL/TLS version to %d\n"), ssl_version); - if (!specify_port) + } + if (!specify_port) { server_port = HTTPS_PORT; + } break; #else /* LIBCURL_FEATURE_SSL */ /* -C -J and -K fall through to here without SSL */ @@ -1640,35 +1710,37 @@ bool process_arguments(int argc, char **argv) { break; #endif /* LIBCURL_FEATURE_SSL */ case MAX_REDIRS_OPTION: - if (!is_intnonneg(optarg)) + if (!is_intnonneg(optarg)) { usage2(_("Invalid max_redirs count"), optarg); - else { + } else { max_depth = atoi(optarg); } break; case 'f': /* onredirect */ - if (!strcmp(optarg, "ok")) + if (!strcmp(optarg, "ok")) { onredirect = STATE_OK; - else if (!strcmp(optarg, "warning")) + } else if (!strcmp(optarg, "warning")) { onredirect = STATE_WARNING; - else if (!strcmp(optarg, "critical")) + } else if (!strcmp(optarg, "critical")) { onredirect = STATE_CRITICAL; - else if (!strcmp(optarg, "unknown")) + } else if (!strcmp(optarg, "unknown")) { onredirect = STATE_UNKNOWN; - else if (!strcmp(optarg, "follow")) + } else if (!strcmp(optarg, "follow")) { onredirect = STATE_DEPENDENT; - else if (!strcmp(optarg, "stickyport")) + } else if (!strcmp(optarg, "stickyport")) { onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST | STICKY_PORT; - else if (!strcmp(optarg, "sticky")) + } else if (!strcmp(optarg, "sticky")) { onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST; - else if (!strcmp(optarg, "follow")) + } else if (!strcmp(optarg, "follow")) { onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_NONE; - else if (!strcmp(optarg, "curl")) + } else if (!strcmp(optarg, "curl")) { onredirect = STATE_DEPENDENT, followmethod = FOLLOW_LIBCURL; - else + } else { usage2(_("Invalid onredirect option"), optarg); - if (verbose >= 2) + } + if (verbose >= 2) { printf(_("* Following redirects set to %s\n"), state_text(onredirect)); + } break; case 'd': /* string or substring */ strncpy(header_expect, optarg, MAX_INPUT_BUFFER - 1); @@ -1706,12 +1778,13 @@ bool process_arguments(int argc, char **argv) { invert_regex = true; break; case STATE_REGEX: - if (!strcasecmp(optarg, "critical")) + if (!strcasecmp(optarg, "critical")) { state_regex = STATE_CRITICAL; - else if (!strcasecmp(optarg, "warning")) + } else if (!strcasecmp(optarg, "warning")) { state_regex = STATE_WARNING; - else + } else { usage2(_("Invalid state-regex option"), optarg); + } break; case '4': address_family = AF_INET; @@ -1732,17 +1805,20 @@ bool process_arguments(int argc, char **argv) { if (tmp == NULL) { printf("Bad format: try \"-m min:max\"\n"); exit(STATE_WARNING); - } else + } else { min_page_len = atoi(tmp); + } tmp = strtok(NULL, ":"); if (tmp == NULL) { printf("Bad format: try \"-m min:max\"\n"); exit(STATE_WARNING); - } else + } else { max_page_len = atoi(tmp); - } else + } + } else { min_page_len = atoi(optarg); + } break; } case 'N': /* no-body */ @@ -1751,20 +1827,21 @@ bool process_arguments(int argc, char **argv) { case 'M': /* max-age */ { int L = strlen(optarg); - if (L && optarg[L - 1] == 'm') + if (L && optarg[L - 1] == 'm') { maximum_age = atoi(optarg) * 60; - else if (L && optarg[L - 1] == 'h') + } else if (L && optarg[L - 1] == 'h') { maximum_age = atoi(optarg) * 60 * 60; - else if (L && optarg[L - 1] == 'd') + } else if (L && optarg[L - 1] == 'd') { maximum_age = atoi(optarg) * 60 * 60 * 24; - else if (L && (optarg[L - 1] == 's' || isdigit(optarg[L - 1]))) + } else if (L && (optarg[L - 1] == 's' || isdigit(optarg[L - 1]))) { maximum_age = atoi(optarg); - else { + } else { fprintf(stderr, "unparsable max-age: %s\n", optarg); exit(STATE_WARNING); } - if (verbose >= 2) + if (verbose >= 2) { printf("* Maximal age of document set to %d seconds\n", maximum_age); + } } break; case 'E': /* show extended perfdata */ show_extended_perfdata = true; @@ -1807,38 +1884,47 @@ bool process_arguments(int argc, char **argv) { c = optind; - if (server_address == NULL && c < argc) + if (server_address == NULL && c < argc) { server_address = strdup(argv[c++]); + } - if (host_name == NULL && c < argc) + if (host_name == NULL && c < argc) { host_name = strdup(argv[c++]); + } if (server_address == NULL) { - if (host_name == NULL) + if (host_name == NULL) { usage4(_("You must specify a server address or host name")); - else + } else { server_address = strdup(host_name); + } } set_thresholds(&thlds, warning_thresholds, critical_thresholds); - if (critical_thresholds && thlds->critical->end > (double)socket_timeout) + if (critical_thresholds && thlds->critical->end > (double)socket_timeout) { socket_timeout = (int)thlds->critical->end + 1; - if (verbose >= 2) + } + if (verbose >= 2) { printf("* Socket timeout set to %ld seconds\n", socket_timeout); + } - if (http_method == NULL) + if (http_method == NULL) { http_method = strdup("GET"); + } - if (client_cert && !client_privkey) + if (client_cert && !client_privkey) { usage4(_("If you use a client certificate you must also specify a private key file")); + } - if (virtual_port == 0) + if (virtual_port == 0) { virtual_port = server_port; - else { - if ((use_ssl && server_port == HTTPS_PORT) || (!use_ssl && server_port == HTTP_PORT)) - if (!specify_port) + } else { + if ((use_ssl && server_port == HTTPS_PORT) || (!use_ssl && server_port == HTTP_PORT)) { + if (!specify_port) { server_port = virtual_port; + } + } } return true; @@ -2113,8 +2199,9 @@ int curlhelp_initwritebuffer(curlhelp_write_curlbuf *buf) { buf->bufsize = DEFAULT_BUFFER_SIZE; buf->buflen = 0; buf->buf = (char *)malloc((size_t)buf->bufsize); - if (buf->buf == NULL) + if (buf->buf == NULL) { return -1; + } return 0; } @@ -2156,8 +2243,9 @@ void curlhelp_freewritebuffer(curlhelp_write_curlbuf *buf) { int curlhelp_initreadbuffer(curlhelp_read_curlbuf *buf, const char *data, size_t datalen) { buf->buflen = datalen; buf->buf = (char *)malloc((size_t)buf->buflen); - if (buf->buf == NULL) + if (buf->buf == NULL) { return -1; + } memcpy(buf->buf, data, datalen); buf->pos = 0; return 0; @@ -2176,11 +2264,13 @@ const char *strrstr2(const char *haystack, const char *needle) { const char *prev_pos; const char *pos; - if (haystack == NULL || needle == NULL) + if (haystack == NULL || needle == NULL) { return NULL; + } - if (haystack[0] == '\0' || needle[0] == '\0') + if (haystack[0] == '\0' || needle[0] == '\0') { return NULL; + } counter = 0; prev_pos = NULL; @@ -2189,15 +2279,17 @@ const char *strrstr2(const char *haystack, const char *needle) { for (;;) { pos = strstr(pos, needle); if (pos == NULL) { - if (counter == 0) + if (counter == 0) { return NULL; + } return prev_pos; } counter++; prev_pos = pos; pos += len; - if (*pos == '\0') + if (*pos == '\0') { return prev_pos; + } } } @@ -2217,13 +2309,15 @@ int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) } first_line_end = strstr(buf, "\r\n"); - if (first_line_end == NULL) + if (first_line_end == NULL) { return -1; + } first_line_len = (size_t)(first_line_end - buf); status_line->first_line = (char *)malloc(first_line_len + 1); - if (status_line->first_line == NULL) + if (status_line->first_line == NULL) { return -1; + } memcpy(status_line->first_line, buf, first_line_len); status_line->first_line[first_line_len] = '\0'; first_line_buf = strdup(status_line->first_line); @@ -2362,8 +2456,9 @@ int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[D } else { time_t srv_data = curl_getdate(server_date, NULL); time_t doc_data = curl_getdate(document_date, NULL); - if (verbose >= 2) + if (verbose >= 2) { printf("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data, document_date, (int)doc_data); + } if (srv_data <= 0) { char tmp[DEFAULT_BUFFER_SIZE]; @@ -2405,10 +2500,12 @@ int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[D } } - if (server_date) + if (server_date) { free(server_date); - if (document_date) + } + if (document_date) { free(document_date); + } return date_result; } @@ -2438,8 +2535,9 @@ int get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_ /* TODO: should we warn if the actual and the reported body length don't match? */ } - if (content_length_s) + if (content_length_s) { free(content_length_s); + } return header_buf->buflen + body_buf->buflen; } @@ -2452,28 +2550,33 @@ curlhelp_ssl_library curlhelp_get_ssl_library(void) { curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN; version_data = curl_version_info(CURLVERSION_NOW); - if (version_data == NULL) + if (version_data == NULL) { return CURLHELP_SSL_LIBRARY_UNKNOWN; + } ssl_version = strdup(version_data->ssl_version); - if (ssl_version == NULL) + if (ssl_version == NULL) { return CURLHELP_SSL_LIBRARY_UNKNOWN; + } library = strtok(ssl_version, "/"); - if (library == NULL) + if (library == NULL) { return CURLHELP_SSL_LIBRARY_UNKNOWN; + } - if (strcmp(library, "OpenSSL") == 0) + if (strcmp(library, "OpenSSL") == 0) { ssl_library = CURLHELP_SSL_LIBRARY_OPENSSL; - else if (strcmp(library, "LibreSSL") == 0) + } else if (strcmp(library, "LibreSSL") == 0) { ssl_library = CURLHELP_SSL_LIBRARY_LIBRESSL; - else if (strcmp(library, "GnuTLS") == 0) + } else if (strcmp(library, "GnuTLS") == 0) { ssl_library = CURLHELP_SSL_LIBRARY_GNUTLS; - else if (strcmp(library, "NSS") == 0) + } else if (strcmp(library, "NSS") == 0) { ssl_library = CURLHELP_SSL_LIBRARY_NSS; + } - if (verbose >= 2) + if (verbose >= 2) { printf("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library, ssl_library); + } free(ssl_version); @@ -2503,14 +2606,16 @@ time_t parse_cert_date(const char *s) { time_t date; char *res; - if (!s) + if (!s) { return -1; + } /* Jan 17 14:25:12 2020 GMT */ res = strptime(s, "%Y-%m-%d %H:%M:%S GMT", &tm); /* Sep 11 12:00:00 2020 GMT */ - if (res == NULL) + if (res == NULL) { strptime(s, "%Y %m %d %H:%M:%S GMT", &tm); + } date = mktime(&tm); return date; @@ -2534,8 +2639,9 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_ char timestamp[50] = ""; int status = STATE_UNKNOWN; - if (verbose >= 2) + if (verbose >= 2) { printf("**** REQUEST CERTIFICATES ****\n"); + } for (i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) { for (slist = cert_ptr->to_certinfo->certinfo[i]; slist; slist = slist->next) { @@ -2563,14 +2669,16 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_ } else if (strncasecmp(slist->data, "Cert:", 5) == 0) { goto HAVE_FIRST_CERT; } - if (verbose >= 2) + if (verbose >= 2) { printf("%d ** %s\n", i, slist->data); + } } } HAVE_FIRST_CERT: - if (verbose >= 2) + if (verbose >= 2) { printf("**** REQUEST CERTIFICATES ****\n"); + } if (!cname_found) { printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject.")); @@ -2597,42 +2705,47 @@ HAVE_FIRST_CERT: setenv("TZ", "GMT", 1); tzset(); strftime(timestamp, 50, "%c %z", localtime(&end_date)); - if (tz) + if (tz) { setenv("TZ", tz, 1); - else + } else { unsetenv("TZ"); + } tzset(); if (days_left > 0 && days_left <= days_till_exp_warn) { printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, days_left, timestamp); - if (days_left > days_till_exp_crit) + if (days_left > days_till_exp_crit) { status = STATE_WARNING; - else + } else { status = STATE_CRITICAL; + } } else if (days_left == 0 && time_left > 0) { - if (time_left >= 3600) + if (time_left >= 3600) { time_remaining = (int)time_left / 3600; - else + } else { time_remaining = (int)time_left / 60; + } printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, time_remaining, time_left >= 3600 ? "hours" : "minutes", timestamp); - if (days_left > days_till_exp_crit) + if (days_left > days_till_exp_crit) { status = STATE_WARNING; - else + } else { status = STATE_CRITICAL; + } } else if (time_left < 0) { printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), host_name, timestamp); status = STATE_CRITICAL; } else if (days_left == 0) { printf(_("%s - Certificate '%s' just expired (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, timestamp); - if (days_left > days_till_exp_crit) + if (days_left > days_till_exp_crit) { status = STATE_WARNING; - else + } else { status = STATE_CRITICAL; + } } else { printf(_("OK - Certificate '%s' will expire on %s.\n"), host_name, timestamp); status = STATE_OK; -- cgit v1.2.3-74-g34f1 From f25a4000b6ffbe0fff1d749ff334b36c77ddc6a2 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 11 Mar 2025 02:02:27 +0100 Subject: Refactor check_curl --- plugins/Makefile.am | 3 +- plugins/check_curl.c | 993 ++++++++++++++++++++---------------------- plugins/check_curl.d/config.h | 141 ++++++ 3 files changed, 607 insertions(+), 530 deletions(-) create mode 100644 plugins/check_curl.d/config.h (limited to 'plugins/check_curl.c') diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 41487131..ce00c392 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -57,7 +57,8 @@ EXTRA_DIST = t \ check_apt.d \ check_by_ssh.d \ check_smtp.d \ - check_dig.d + check_dig.d \ + check_curl.d PLUGINHDRS = common.h diff --git a/plugins/check_curl.c b/plugins/check_curl.c index cf755316..4806bf14 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -37,6 +37,9 @@ const char *progname = "check_curl"; const char *copyright = "2006-2024"; const char *email = "devel@monitoring-plugins.org"; +#include "check_curl.d/config.h" +#include "states.h" +#include "thresholds.h" #include #include @@ -65,27 +68,9 @@ const char *email = "devel@monitoring-plugins.org"; #define MAKE_LIBCURL_VERSION(major, minor, patch) ((major) * 0x10000 + (minor) * 0x100 + (patch)) -#define DEFAULT_BUFFER_SIZE 2048 -#define DEFAULT_SERVER_URL "/" -#define HTTP_EXPECT "HTTP/" -#define INET_ADDR_MAX_SIZE INET6_ADDRSTRLEN +#define INET_ADDR_MAX_SIZE INET6_ADDRSTRLEN enum { MAX_IPV4_HOSTLENGTH = 255, - HTTP_PORT = 80, - HTTPS_PORT = 443, - MAX_PORT = 65535, - DEFAULT_MAX_REDIRS = 15 -}; - -enum { - STICKY_NONE = 0, - STICKY_HOST = 1, - STICKY_PORT = 2 -}; - -enum { - FOLLOW_HTTP_CURL = 0, - FOLLOW_LIBCURL = 1 }; /* for buffers for header and body */ @@ -125,46 +110,14 @@ typedef enum curlhelp_ssl_library { enum { REGS = 2, - MAX_RE_SIZE = 1024 }; + #include "regex.h" -static regex_t preg; -static regmatch_t pmatch[REGS]; -static char regexp[MAX_RE_SIZE]; -static int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE; -static int errcode; -static bool invert_regex = false; -static int state_regex = STATE_CRITICAL; - -static char *server_address = NULL; -static char *host_name = NULL; -static char *server_url = 0; -static struct curl_slist *server_ips = NULL; -static bool specify_port = false; -static unsigned short server_port = HTTP_PORT; -static unsigned short virtual_port = 0; -static int host_name_length; -static char output_header_search[30] = ""; -static char output_string_search[30] = ""; -static char *warning_thresholds = NULL; -static char *critical_thresholds = NULL; -static int days_till_exp_warn, days_till_exp_crit; -static thresholds *thlds; -static char user_agent[DEFAULT_BUFFER_SIZE]; -static int verbose = 0; -static bool show_extended_perfdata = false; -static bool show_body = false; -static int min_page_len = 0; -static int max_page_len = 0; -static int redir_depth = 0; -static int max_depth = DEFAULT_MAX_REDIRS; -static char *http_method = NULL; -static char *http_post_data = NULL; -static char *http_content_type = NULL; -static CURL *curl; + +// Globals static bool curl_global_initialized = false; static bool curl_easy_initialized = false; -static struct curl_slist *header_list = NULL; +static int verbose = 0; static bool body_buf_initialized = false; static curlhelp_write_curlbuf body_buf; static bool header_buf_initialized = false; @@ -173,69 +126,45 @@ static bool status_line_initialized = false; static curlhelp_statusline status_line; static bool put_buf_initialized = false; static curlhelp_read_curlbuf put_buf; -static char http_header[DEFAULT_BUFFER_SIZE]; + +static struct curl_slist *server_ips = NULL; // TODO maybe unused +static int redir_depth = 0; // Maybe global +static CURL *curl; +static struct curl_slist *header_list = NULL; static long code; -static long socket_timeout = DEFAULT_SOCKET_TIMEOUT; -static double total_time; -static double time_connect; -static double time_appconnect; -static double time_headers; -static double time_firstbyte; static char errbuf[MAX_INPUT_BUFFER]; -static CURLcode res; -static char url[DEFAULT_BUFFER_SIZE]; static char msg[DEFAULT_BUFFER_SIZE]; -static char perfstring[DEFAULT_BUFFER_SIZE]; -static char header_expect[MAX_INPUT_BUFFER] = ""; -static char string_expect[MAX_INPUT_BUFFER] = ""; -static char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT; -static int server_expect_yn = 0; -static char user_auth[MAX_INPUT_BUFFER] = ""; -static char proxy_auth[MAX_INPUT_BUFFER] = ""; -static char **http_opt_headers; -static int http_opt_headers_count = 0; -static bool display_html = false; -static int onredirect = STATE_OK; -static int followmethod = FOLLOW_HTTP_CURL; -static int followsticky = STICKY_NONE; -static bool use_ssl = false; -static bool check_cert = false; -static bool continue_after_check_cert = false; typedef union { struct curl_slist *to_info; struct curl_certinfo *to_certinfo; } cert_ptr_union; static cert_ptr_union cert_ptr; -static int ssl_version = CURL_SSLVERSION_DEFAULT; -static char *client_cert = NULL; -static char *client_privkey = NULL; -static char *ca_cert = NULL; -static bool verify_peer_and_host = false; static bool is_openssl_callback = false; static bool add_sslctx_verify_fun = false; + #if defined(HAVE_SSL) && defined(USE_OPENSSL) static X509 *cert = NULL; #endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ -static bool no_body = false; -static int maximum_age = -1; + static int address_family = AF_UNSPEC; static curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN; -static int curl_http_version = CURL_HTTP_VERSION_NONE; -static bool automatic_decompression = false; -static char *cookie_jar_file = NULL; -static bool haproxy_protocol = false; -static bool process_arguments(int /*argc*/, char ** /*argv*/); +typedef struct { + int errorcode; + check_curl_config config; +} check_curl_config_wrapper; +static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); + static void handle_curl_option_return_code(CURLcode res, const char *option); -static int check_http(void); -static void redir(curlhelp_write_curlbuf * /*header_buf*/); -static char *perfd_time(double elapsed_time); -static char *perfd_time_connect(double elapsed_time_connect); -static char *perfd_time_ssl(double elapsed_time_ssl); -static char *perfd_time_firstbyte(double elapsed_time_firstbyte); -static char *perfd_time_headers(double elapsed_time_headers); -static char *perfd_time_transfer(double elapsed_time_transfer); -static char *perfd_size(int page_len); +static mp_state_enum check_http(check_curl_config /*config*/); +static void redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/); +static char *perfd_time(double elapsed_time, thresholds * /*thlds*/, long /*socket_timeout*/); +static char *perfd_time_connect(double elapsed_time_connect, long /*socket_timeout*/); +static char *perfd_time_ssl(double elapsed_time_ssl, long /*socket_timeout*/); +static char *perfd_time_firstbyte(double elapsed_time_firstbyte, long /*socket_timeout*/); +static char *perfd_time_headers(double elapsed_time_headers, long /*socket_timeout*/); +static char *perfd_time_transfer(double elapsed_time_transfer, long /*socket_timeout*/); +static char *perfd_size(int page_len, int /*min_page_len*/); static void print_help(void); void print_usage(void); static void print_curl_version(void); @@ -252,7 +181,7 @@ int net_noopenssl_check_certificate(cert_ptr_union *, int, int); static int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * /*status_line*/); static void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/); static char *get_header_value(const struct phr_header *headers, size_t nof_headers, const char *header); -static int check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, char (*msg)[DEFAULT_BUFFER_SIZE]); +static int check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, char (*msg)[DEFAULT_BUFFER_SIZE], int /*maximum_age*/); static int get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_write_curlbuf *body_buf); #if defined(HAVE_SSL) && defined(USE_OPENSSL) @@ -262,8 +191,6 @@ int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int static void test_file(char * /*path*/); int main(int argc, char **argv) { - int result = STATE_UNKNOWN; - setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); @@ -271,21 +198,27 @@ int main(int argc, char **argv) { /* Parse extra opts if any */ argv = np_extra_opts(&argc, argv, progname); - /* set defaults */ - snprintf(user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", progname, NP_VERSION, VERSION, curl_version()); - /* parse arguments */ - if (process_arguments(argc, argv) == false) { + check_curl_config_wrapper tmp_config = process_arguments(argc, argv); + if (tmp_config.errorcode == ERROR) { usage4(_("Could not parse arguments")); } - if (display_html) { - printf("", use_ssl ? "https" : "http", host_name ? host_name : server_address, - virtual_port ? virtual_port : server_port, server_url); + const check_curl_config config = tmp_config.config; + + /* set defaults */ + if (config.user_agent == NULL) { + snprintf(config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", progname, NP_VERSION, VERSION, + curl_version()); } - result = check_http(); - return result; + if (config.display_html) { + printf("", config.use_ssl ? "https" : "http", + config.host_name ? config.host_name : config.server_address, config.virtual_port ? config.virtual_port : config.server_port, + config.server_url); + } + + exit(check_http(config)); } #ifdef HAVE_SSL @@ -360,13 +293,13 @@ static char *string_statuscode(int major, int minor) { /* Checks if the server 'reply' is one of the expected 'statuscodes' */ static int expected_statuscode(const char *reply, const char *statuscodes) { char *expected; - char *code; - int result = 0; if ((expected = strdup(statuscodes)) == NULL) { die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); } + char *code; + int result = 0; for (code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) { if (strstr(reply, code) != NULL) { result = 1; @@ -387,26 +320,25 @@ void handle_curl_option_return_code(CURLcode res, const char *option) { } int lookup_host(const char *host, char *buf, size_t buflen) { - struct addrinfo hints, *res, *result; - char addrstr[100]; - size_t addrstr_len; - int errcode; - void *ptr = {0}; - size_t buflen_remaining = buflen - 1; - + struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = address_family; hints.ai_socktype = SOCK_STREAM; hints.ai_flags |= AI_CANONNAME; - errcode = getaddrinfo(host, NULL, &hints, &result); + struct addrinfo *result; + int errcode = getaddrinfo(host, NULL, &hints, &result); if (errcode != 0) { return errcode; } strcpy(buf, ""); - res = result; + struct addrinfo *res = result; + size_t buflen_remaining = buflen - 1; + size_t addrstr_len; + char addrstr[100]; + void *ptr = {0}; while (res) { switch (res->ai_family) { case AF_INET: @@ -468,16 +400,7 @@ static void cleanup(void) { put_buf_initialized = false; } -int check_http(void) { - int result = STATE_OK; - int result_ssl = STATE_OK; - int page_len = 0; - int i; - char *force_host_header = NULL; - struct curl_slist *host = NULL; - char addrstr[DEFAULT_BUFFER_SIZE / 2]; - char dnscache[DEFAULT_BUFFER_SIZE]; - +mp_state_enum check_http(check_curl_config config) { /* initialize curl */ if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n"); @@ -499,7 +422,7 @@ int check_http(void) { /* print everything on stdout like check_http would do */ handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_STDERR, stdout), "CURLOPT_STDERR"); - if (automatic_decompression) + if (config.automatic_decompression) #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""), "CURLOPT_ACCEPT_ENCODING"); #else @@ -528,23 +451,27 @@ int check_http(void) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf), "CURLOPT_ERRORBUFFER"); /* set timeouts */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, socket_timeout), "CURLOPT_CONNECTTIMEOUT"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_TIMEOUT, socket_timeout), "CURLOPT_TIMEOUT"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, config.socket_timeout), "CURLOPT_CONNECTTIMEOUT"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_TIMEOUT, config.socket_timeout), "CURLOPT_TIMEOUT"); /* enable haproxy protocol */ - if (haproxy_protocol) { + if (config.haproxy_protocol) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HAPROXYPROTOCOL, 1L), "CURLOPT_HAPROXYPROTOCOL"); } // fill dns resolve cache to make curl connect to the given server_address instead of the host_name, only required for ssl, because we // use the host_name later on to make SNI happy - if (use_ssl && host_name != NULL) { - if ((res = lookup_host(server_address, addrstr, DEFAULT_BUFFER_SIZE / 2)) != 0) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), server_address, res, - gai_strerror(res)); + struct curl_slist *host = NULL; + char dnscache[DEFAULT_BUFFER_SIZE]; + char addrstr[DEFAULT_BUFFER_SIZE / 2]; + if (config.use_ssl && config.host_name != NULL) { + CURLcode res; + if ((res = lookup_host(config.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2)) != 0) { + snprintf(msg, DEFAULT_BUFFER_SIZE, _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), + config.server_address, res, gai_strerror(res)); die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } - snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", host_name, server_port, addrstr); + snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", config.host_name, config.server_port, addrstr); host = curl_slist_append(NULL, dnscache); curl_easy_setopt(curl, CURLOPT_RESOLVE, host); if (verbose >= 1) { @@ -554,19 +481,21 @@ int check_http(void) { // If server_address is an IPv6 address it must be surround by square brackets struct in6_addr tmp_in_addr; - if (inet_pton(AF_INET6, server_address, &tmp_in_addr) == 1) { - char *new_server_address = malloc(strlen(server_address) + 3); + if (inet_pton(AF_INET6, config.server_address, &tmp_in_addr) == 1) { + char *new_server_address = malloc(strlen(config.server_address) + 3); if (new_server_address == NULL) { die(STATE_UNKNOWN, "HTTP UNKNOWN - Unable to allocate memory\n"); } - snprintf(new_server_address, strlen(server_address) + 3, "[%s]", server_address); - free(server_address); - server_address = new_server_address; + snprintf(new_server_address, strlen(config.server_address) + 3, "[%s]", config.server_address); + free(config.server_address); + config.server_address = new_server_address; } /* compose URL: use the address we want to connect to, set Host: header later */ - snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", use_ssl ? "https" : "http", - (use_ssl & (host_name != NULL)) ? host_name : server_address, server_port, server_url); + char url[DEFAULT_BUFFER_SIZE]; + snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", config.use_ssl ? "https" : "http", + (config.use_ssl & (config.host_name != NULL)) ? config.host_name : config.server_address, config.server_port, + config.server_url); if (verbose >= 1) { printf("* curl CURLOPT_URL: %s\n", url); @@ -574,50 +503,52 @@ int check_http(void) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, url), "CURLOPT_URL"); /* extract proxy information for legacy proxy https requests */ - if (!strcmp(http_method, "CONNECT") || strstr(server_url, "http") == server_url) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXY, server_address), "CURLOPT_PROXY"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYPORT, (long)server_port), "CURLOPT_PROXYPORT"); + if (!strcmp(config.http_method, "CONNECT") || strstr(config.server_url, "http") == config.server_url) { + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXY, config.server_address), "CURLOPT_PROXY"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYPORT, (long)config.server_port), "CURLOPT_PROXYPORT"); if (verbose >= 2) { - printf("* curl CURLOPT_PROXY: %s:%d\n", server_address, server_port); + printf("* curl CURLOPT_PROXY: %s:%d\n", config.server_address, config.server_port); } - http_method = "GET"; - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, server_url), "CURLOPT_URL"); + config.http_method = "GET"; + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, config.server_url), "CURLOPT_URL"); } /* disable body for HEAD request */ - if (http_method && !strcmp(http_method, "HEAD")) { - no_body = true; + if (config.http_method && !strcmp(config.http_method, "HEAD")) { + config.no_body = true; } /* set HTTP protocol version */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, curl_http_version), "CURLOPT_HTTP_VERSION"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, config.curl_http_version), "CURLOPT_HTTP_VERSION"); /* set HTTP method */ - if (http_method) { - if (!strcmp(http_method, "POST")) { + if (config.http_method) { + if (!strcmp(config.http_method, "POST")) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POST, 1), "CURLOPT_POST"); - } else if (!strcmp(http_method, "PUT")) { + } else if (!strcmp(config.http_method, "PUT")) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD"); } else { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, http_method), "CURLOPT_CUSTOMREQUEST"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, config.http_method), "CURLOPT_CUSTOMREQUEST"); } } + char *force_host_header = NULL; /* check if Host header is explicitly set in options */ - if (http_opt_headers_count) { - for (i = 0; i < http_opt_headers_count; i++) { - if (strncmp(http_opt_headers[i], "Host:", 5) == 0) { - force_host_header = http_opt_headers[i]; + if (config.http_opt_headers_count) { + for (int i = 0; i < config.http_opt_headers_count; i++) { + if (strncmp(config.http_opt_headers[i], "Host:", 5) == 0) { + force_host_header = config.http_opt_headers[i]; } } } /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in anyway */ - if (host_name != NULL && force_host_header == NULL) { - if ((virtual_port != HTTP_PORT && !use_ssl) || (virtual_port != HTTPS_PORT && use_ssl)) { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", host_name, virtual_port); + char http_header[DEFAULT_BUFFER_SIZE]; + if (config.host_name != NULL && force_host_header == NULL) { + if ((config.virtual_port != HTTP_PORT && !config.use_ssl) || (config.virtual_port != HTTPS_PORT && config.use_ssl)) { + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", config.host_name, config.virtual_port); } else { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", host_name); + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", config.host_name); } header_list = curl_slist_append(header_list, http_header); } @@ -628,9 +559,9 @@ int check_http(void) { /* attach additional headers supplied by the user */ /* optionally send any other header tag */ - if (http_opt_headers_count) { - for (i = 0; i < http_opt_headers_count; i++) { - header_list = curl_slist_append(header_list, http_opt_headers[i]); + if (config.http_opt_headers_count) { + for (int i = 0; i < config.http_opt_headers_count; i++) { + header_list = curl_slist_append(header_list, config.http_opt_headers[i]); } /* This cannot be free'd here because a redirection will then try to access this and segfault */ /* Covered in a testcase in tests/check_http.t */ @@ -643,21 +574,21 @@ int check_http(void) { #ifdef LIBCURL_FEATURE_SSL /* set SSL version, warn about insecure or unsupported versions */ - if (use_ssl) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLVERSION, ssl_version), "CURLOPT_SSLVERSION"); + if (config.use_ssl) { + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLVERSION, config.ssl_version), "CURLOPT_SSLVERSION"); } /* client certificate and key to present to server (SSL) */ - if (client_cert) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLCERT, client_cert), "CURLOPT_SSLCERT"); + if (config.client_cert) { + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLCERT, config.client_cert), "CURLOPT_SSLCERT"); } - if (client_privkey) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLKEY, client_privkey), "CURLOPT_SSLKEY"); + if (config.client_privkey) { + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLKEY, config.client_privkey), "CURLOPT_SSLKEY"); } - if (ca_cert) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CAINFO, ca_cert), "CURLOPT_CAINFO"); + if (config.ca_cert) { + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CAINFO, config.ca_cert), "CURLOPT_CAINFO"); } - if (ca_cert || verify_peer_and_host) { + if (config.ca_cert || config.verify_peer_and_host) { /* per default if we have a CA verify both the peer and the * hostname in the certificate, can be switched off later */ handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1), "CURLOPT_SSL_VERIFYPEER"); @@ -675,7 +606,7 @@ int check_http(void) { ssl_library = curlhelp_get_ssl_library(); /* try hard to get a stack of certificates to verify against */ - if (check_cert) { + if (config.check_cert) { # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) /* inform curl to report back certificates */ switch (ssl_library) { @@ -740,16 +671,16 @@ int check_http(void) { #endif /* LIBCURL_FEATURE_SSL */ /* set default or user-given user agent identification */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent), "CURLOPT_USERAGENT"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERAGENT, config.user_agent), "CURLOPT_USERAGENT"); /* proxy-authentication */ - if (strcmp(proxy_auth, "")) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy_auth), "CURLOPT_PROXYUSERPWD"); + if (strcmp(config.proxy_auth, "")) { + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, config.proxy_auth), "CURLOPT_PROXYUSERPWD"); } /* authentication */ - if (strcmp(user_auth, "")) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERPWD, user_auth), "CURLOPT_USERPWD"); + if (strcmp(config.user_auth, "")) { + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERPWD, config.user_auth), "CURLOPT_USERPWD"); } /* TODO: parameter auth method, bitfield of following methods: @@ -768,14 +699,14 @@ int check_http(void) { */ /* handle redirections */ - if (onredirect == STATE_DEPENDENT) { - if (followmethod == FOLLOW_LIBCURL) { + if (config.onredirect == STATE_DEPENDENT) { + if (config.followmethod == FOLLOW_LIBCURL) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1), "CURLOPT_FOLLOWLOCATION"); /* default -1 is infinite, not good, could lead to zombie plugins! Setting it to one bigger than maximal limit to handle errors nicely below */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_MAXREDIRS, max_depth + 1), "CURLOPT_MAXREDIRS"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_MAXREDIRS, config.max_depth + 1), "CURLOPT_MAXREDIRS"); /* for now allow only http and https (we are a http(s) check plugin in the end) */ #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 85, 0) @@ -799,7 +730,7 @@ int check_http(void) { } /* no-body */ - if (no_body) { + if (config.no_body) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_NOBODY, 1), "CURLOPT_NOBODY"); } @@ -819,48 +750,48 @@ int check_http(void) { #endif /* either send http POST data (any data, not only POST)*/ - if (!strcmp(http_method, "POST") || !strcmp(http_method, "PUT")) { + if (!strcmp(config.http_method, "POST") || !strcmp(config.http_method, "PUT")) { /* set content of payload for POST and PUT */ - if (http_content_type) { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", http_content_type); + if (config.http_content_type) { + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", config.http_content_type); header_list = curl_slist_append(header_list, http_header); } /* NULL indicates "HTTP Continue" in libcurl, provide an empty string * in case of no POST/PUT data */ - if (!http_post_data) { - http_post_data = ""; + if (!config.http_post_data) { + config.http_post_data = ""; } - if (!strcmp(http_method, "POST")) { + if (!strcmp(config.http_method, "POST")) { /* POST method, set payload with CURLOPT_POSTFIELDS */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POSTFIELDS, http_post_data), "CURLOPT_POSTFIELDS"); - } else if (!strcmp(http_method, "PUT")) { + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POSTFIELDS, config.http_post_data), "CURLOPT_POSTFIELDS"); + } else if (!strcmp(config.http_method, "PUT")) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_READFUNCTION, (curl_read_callback)curlhelp_buffer_read_callback), "CURLOPT_READFUNCTION"); - if (curlhelp_initreadbuffer(&put_buf, http_post_data, strlen(http_post_data)) < 0) { + if (curlhelp_initreadbuffer(&put_buf, config.http_post_data, strlen(config.http_post_data)) < 0) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating read buffer for PUT\n"); } put_buf_initialized = true; handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_READDATA, (void *)&put_buf), "CURLOPT_READDATA"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_INFILESIZE, (curl_off_t)strlen(http_post_data)), + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_INFILESIZE, (curl_off_t)strlen(config.http_post_data)), "CURLOPT_INFILESIZE"); } } /* cookie handling */ - if (cookie_jar_file != NULL) { + if (config.cookie_jar_file != NULL) { /* enable reading cookies from a file, and if the filename is an empty string, only enable the curl cookie engine */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookie_jar_file), "CURLOPT_COOKIEFILE"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEFILE, config.cookie_jar_file), "CURLOPT_COOKIEFILE"); /* now enable saving cookies to a file, but only if the filename is not an empty string, since writing it would fail */ - if (*cookie_jar_file) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookie_jar_file), "CURLOPT_COOKIEJAR"); + if (*config.cookie_jar_file) { + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEJAR, config.cookie_jar_file), "CURLOPT_COOKIEJAR"); } } /* do the request */ - res = curl_easy_perform(curl); + CURLcode res = curl_easy_perform(curl); - if (verbose >= 2 && http_post_data) { - printf("**** REQUEST CONTENT ****\n%s\n", http_post_data); + if (verbose >= 2 && config.http_post_data) { + printf("**** REQUEST CONTENT ****\n%s\n", config.http_post_data); } /* free header and server IP resolve lists, we don't need it anymore */ @@ -875,22 +806,23 @@ int check_http(void) { /* Curl errors, result in critical Nagios state */ if (res != CURLE_OK) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"), server_port, - res, errbuf[0] ? errbuf : curl_easy_strerror(res)); + snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"), + config.server_port, res, errbuf[0] ? errbuf : curl_easy_strerror(res)); die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } + int result_ssl = STATE_OK; /* certificate checks */ #ifdef LIBCURL_FEATURE_SSL - if (use_ssl) { - if (check_cert) { + if (config.use_ssl) { + if (config.check_cert) { if (is_openssl_callback) { # ifdef USE_OPENSSL /* check certificate with OpenSSL functions, curl has been built against OpenSSL * and we actually have OpenSSL in the monitoring tools */ - result_ssl = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit); - if (!continue_after_check_cert) { + result_ssl = np_net_ssl_check_certificate(cert, config.days_till_exp_warn, config.days_till_exp_crit); + if (!config.continue_after_check_cert) { return result_ssl; } # else /* USE_OPENSSL */ @@ -898,7 +830,6 @@ int check_http(void) { "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL callback used and not linked against OpenSSL\n"); # endif /* USE_OPENSSL */ } else { - int i; struct curl_slist *slist; cert_ptr.to_info = NULL; @@ -909,7 +840,7 @@ int check_http(void) { * We only check the first certificate and assume it's the one of the server */ const char *raw_cert = NULL; - for (i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) { + for (int i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) { for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) { if (verbose >= 2) { printf("%d ** %s\n", i, slist->data); @@ -934,8 +865,8 @@ int check_http(void) { die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } BIO_free(cert_BIO); - result_ssl = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit); - if (!continue_after_check_cert) { + result_ssl = np_net_ssl_check_certificate(cert, config.days_till_exp_warn, config.days_till_exp_crit); + if (!config.continue_after_check_cert) { return result_ssl; } # else /* USE_OPENSSL */ @@ -960,20 +891,30 @@ int check_http(void) { /* we got the data and we executed the request in a given time, so we can append * performance data to the answer always */ + double total_time; handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total_time), "CURLINFO_TOTAL_TIME"); - page_len = get_content_length(&header_buf, &body_buf); - if (show_extended_perfdata) { + int page_len = get_content_length(&header_buf, &body_buf); + char perfstring[DEFAULT_BUFFER_SIZE]; + if (config.show_extended_perfdata) { + double time_connect; handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &time_connect), "CURLINFO_CONNECT_TIME"); + double time_appconnect; handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), "CURLINFO_APPCONNECT_TIME"); + double time_headers; handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, &time_headers), "CURLINFO_PRETRANSFER_TIME"); + double time_firstbyte; handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), "CURLINFO_STARTTRANSFER_TIME"); - snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s", perfd_time(total_time), perfd_size(page_len), - perfd_time_connect(time_connect), use_ssl ? perfd_time_ssl(time_appconnect - time_connect) : "", - perfd_time_headers(time_headers - time_appconnect), perfd_time_firstbyte(time_firstbyte - time_headers), - perfd_time_transfer(total_time - time_firstbyte)); + + snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s", perfd_time(total_time, config.thlds, config.socket_timeout), + perfd_size(page_len, config.min_page_len), perfd_time_connect(time_connect, config.socket_timeout), + config.use_ssl ? perfd_time_ssl(time_appconnect - time_connect, config.socket_timeout) : "", + perfd_time_headers(time_headers - time_appconnect, config.socket_timeout), + perfd_time_firstbyte(time_firstbyte - time_headers, config.socket_timeout), + perfd_time_transfer(total_time - time_firstbyte, config.socket_timeout)); } else { - snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s", perfd_time(total_time), perfd_size(page_len)); + snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s", perfd_time(total_time, config.thlds, config.socket_timeout), + perfd_size(page_len, config.min_page_len)); } /* return a CRITICAL status if we couldn't read any data */ @@ -997,22 +938,23 @@ int check_http(void) { /* print status line, header, body if verbose */ if (verbose >= 2) { - printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header_buf.buf, (no_body ? " [[ skipped ]]" : body_buf.buf)); + printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header_buf.buf, (config.no_body ? " [[ skipped ]]" : body_buf.buf)); } /* make sure the status line matches the response we are looking for */ - if (!expected_statuscode(status_line.first_line, server_expect)) { - if (server_port == HTTP_PORT) { + if (!expected_statuscode(status_line.first_line, config.server_expect)) { + if (config.server_port == HTTP_PORT) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), status_line.first_line); } else { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), server_port, + snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), config.server_port, status_line.first_line); } - die(STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg, show_body ? "\n" : "", show_body ? body_buf.buf : ""); + die(STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg, config.show_body ? "\n" : "", config.show_body ? body_buf.buf : ""); } - if (server_expect_yn) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), server_expect); + int result = STATE_OK; + if (config.server_expect_yn) { + snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), config.server_expect); if (verbose) { printf("%s\n", msg); } @@ -1029,22 +971,22 @@ int check_http(void) { result = STATE_WARNING; /* check redirected page if specified */ } else if (code >= 300) { - if (onredirect == STATE_DEPENDENT) { - if (followmethod == FOLLOW_LIBCURL) { + if (config.onredirect == STATE_DEPENDENT) { + if (config.followmethod == FOLLOW_LIBCURL) { code = status_line.http_code; } else { /* old check_http style redirection, if we come * back here, we are in the same status as with * the libcurl method */ - redir(&header_buf); + redir(&header_buf, config); } } else { /* this is a specific code in the command line to * be returned when a redirection is encountered */ } - result = max_state_alt(onredirect, result); + result = max_state_alt(config.onredirect, result); /* all other codes are considered ok */ } else { result = STATE_OK; @@ -1052,13 +994,13 @@ int check_http(void) { } /* libcurl redirection internally, handle error states here */ - if (followmethod == FOLLOW_LIBCURL) { + if (config.followmethod == FOLLOW_LIBCURL) { handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &redir_depth), "CURLINFO_REDIRECT_COUNT"); if (verbose >= 2) { printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth); } - if (redir_depth > max_depth) { - snprintf(msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl", max_depth); + if (redir_depth > config.max_depth) { + snprintf(msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl", config.max_depth); die(STATE_WARNING, "HTTP WARNING - %s", msg); } } @@ -1069,16 +1011,17 @@ int check_http(void) { string_statuscode(status_line.http_major, status_line.http_minor), status_line.http_code, status_line.msg, code); } - if (maximum_age >= 0) { - result = max_state_alt(check_document_dates(&header_buf, &msg), result); + if (config.maximum_age >= 0) { + result = max_state_alt(check_document_dates(&header_buf, &msg, config.maximum_age), result); } /* Page and Header content checks go here */ - if (strlen(header_expect)) { - if (!strstr(header_buf.buf, header_expect)) { + if (strlen(config.header_expect)) { + if (!strstr(header_buf.buf, config.header_expect)) { - strncpy(&output_header_search[0], header_expect, sizeof(output_header_search)); + char output_header_search[30] = ""; + strncpy(&output_header_search[0], config.header_expect, sizeof(output_header_search)); if (output_header_search[sizeof(output_header_search) - 1] != '\0') { bcopy("...", &output_header_search[sizeof(output_header_search) - 4], 4); @@ -1087,7 +1030,8 @@ int check_http(void) { char tmp[DEFAULT_BUFFER_SIZE]; snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sheader '%s' not found on '%s://%s:%d%s', "), msg, output_header_search, - use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url); + config.use_ssl ? "https" : "http", config.host_name ? config.host_name : config.server_address, config.server_port, + config.server_url); strcpy(msg, tmp); @@ -1095,10 +1039,11 @@ int check_http(void) { } } - if (strlen(string_expect)) { - if (!strstr(body_buf.buf, string_expect)) { + if (strlen(config.string_expect)) { + if (!strstr(body_buf.buf, config.string_expect)) { - strncpy(&output_string_search[0], string_expect, sizeof(output_string_search)); + char output_string_search[30] = ""; + strncpy(&output_string_search[0], config.string_expect, sizeof(output_string_search)); if (output_string_search[sizeof(output_string_search) - 1] != '\0') { bcopy("...", &output_string_search[sizeof(output_string_search) - 4], 4); @@ -1107,7 +1052,8 @@ int check_http(void) { char tmp[DEFAULT_BUFFER_SIZE]; snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sstring '%s' not found on '%s://%s:%d%s', "), msg, output_string_search, - use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url); + config.use_ssl ? "https" : "http", config.host_name ? config.host_name : config.server_address, config.server_port, + config.server_url); strcpy(msg, tmp); @@ -1115,13 +1061,15 @@ int check_http(void) { } } - if (strlen(regexp)) { - errcode = regexec(&preg, body_buf.buf, REGS, pmatch, 0); - if ((errcode == 0 && !invert_regex) || (errcode == REG_NOMATCH && invert_regex)) { + if (strlen(config.regexp)) { + regex_t preg; + regmatch_t pmatch[REGS]; + int errcode = regexec(&preg, body_buf.buf, REGS, pmatch, 0); + if ((errcode == 0 && !config.invert_regex) || (errcode == REG_NOMATCH && config.invert_regex)) { /* OK - No-op to avoid changing the logic around it */ result = max_state_alt(STATE_OK, result); - } else if ((errcode == REG_NOMATCH && !invert_regex) || (errcode == 0 && invert_regex)) { - if (!invert_regex) { + } else if ((errcode == REG_NOMATCH && !config.invert_regex) || (errcode == 0 && config.invert_regex)) { + if (!config.invert_regex) { char tmp[DEFAULT_BUFFER_SIZE]; snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spattern not found, "), msg); @@ -1133,7 +1081,7 @@ int check_http(void) { snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spattern found, "), msg); strcpy(msg, tmp); } - result = state_regex; + result = config.state_regex; } else { regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); @@ -1146,7 +1094,7 @@ int check_http(void) { } /* make sure the page is of an appropriate size */ - if ((max_page_len > 0) && (page_len > max_page_len)) { + if ((config.max_page_len > 0) && (page_len > config.max_page_len)) { char tmp[DEFAULT_BUFFER_SIZE]; snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %d too large, "), msg, page_len); @@ -1155,7 +1103,7 @@ int check_http(void) { result = max_state_alt(STATE_WARNING, result); - } else if ((min_page_len > 0) && (page_len < min_page_len)) { + } else if ((config.min_page_len > 0) && (page_len < config.min_page_len)) { char tmp[DEFAULT_BUFFER_SIZE]; snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %d too small, "), msg, page_len); @@ -1164,7 +1112,7 @@ int check_http(void) { } /* -w, -c: check warning and critical level */ - result = max_state_alt(get_status(total_time, thlds), result); + result = max_state_alt(get_status(total_time, config.thlds), result); /* Cut-off trailing characters */ if (strlen(msg) >= 2) { @@ -1178,8 +1126,8 @@ int check_http(void) { /* TODO: separate _() msg and status code: die (result, "HTTP %s: %s\n", state_text(result), msg); */ die(max_state_alt(result, result_ssl), "HTTP %s: %s %d %s%s%s - %d bytes in %.3f second response time %s|%s\n%s%s", state_text(result), string_statuscode(status_line.http_major, status_line.http_minor), status_line.http_code, status_line.msg, - strlen(msg) > 0 ? " - " : "", msg, page_len, total_time, (display_html ? "" : ""), perfstring, (show_body ? body_buf.buf : ""), - (show_body ? "\n" : "")); + strlen(msg) > 0 ? " - " : "", msg, page_len, total_time, (config.display_html ? "" : ""), perfstring, + (config.show_body ? body_buf.buf : ""), (config.show_body ? "\n" : "")); return max_state_alt(result, result_ssl); } @@ -1204,18 +1152,11 @@ char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) { return buf; } -void redir(curlhelp_write_curlbuf *header_buf) { - char *location = NULL; +void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config) { curlhelp_statusline status_line; struct phr_header headers[255]; - size_t nof_headers = 255; size_t msglen; - char buf[DEFAULT_BUFFER_SIZE]; - char ipstr[INET_ADDR_MAX_SIZE]; - int new_port; - char *new_host; - char *new_url; - + size_t nof_headers = 255; int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor, &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0); @@ -1223,15 +1164,15 @@ void redir(curlhelp_write_curlbuf *header_buf) { die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); } - location = get_header_value(headers, nof_headers, "location"); + char *location = get_header_value(headers, nof_headers, "location"); if (verbose >= 2) { printf(_("* Seen redirect location %s\n"), location); } - if (++redir_depth > max_depth) { - die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s%s\n"), max_depth, location, - (display_html ? "" : "")); + if (++redir_depth > config.max_depth) { + die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s%s\n"), config.max_depth, location, + (config.display_html ? "" : "")); } UriParserStateA state; @@ -1239,12 +1180,15 @@ void redir(curlhelp_write_curlbuf *header_buf) { state.uri = &uri; if (uriParseUriA(&state, location) != URI_SUCCESS) { if (state.errorCode == URI_ERROR_SYNTAX) { - die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location '%s'%s\n"), location, (display_html ? "" : "")); + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location '%s'%s\n"), location, + (config.display_html ? "" : "")); } else if (state.errorCode == URI_ERROR_MALLOC) { die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); } } + char ipstr[INET_ADDR_MAX_SIZE]; + char buf[DEFAULT_BUFFER_SIZE]; if (verbose >= 2) { printf(_("** scheme: %s\n"), uri_string(uri.scheme, buf, DEFAULT_BUFFER_SIZE)); printf(_("** host: %s\n"), uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE)); @@ -1274,35 +1218,34 @@ void redir(curlhelp_write_curlbuf *header_buf) { } if (uri.scheme.first) { - if (!uri_strcmp(uri.scheme, "https")) { - use_ssl = true; - } else { - use_ssl = false; - } + config.use_ssl = (bool)(!uri_strcmp(uri.scheme, "https")); } /* we do a sloppy test here only, because uriparser would have failed * above, if the port would be invalid, we just check for MAX_PORT */ + int new_port; if (uri.portText.first) { new_port = atoi(uri_string(uri.portText, buf, DEFAULT_BUFFER_SIZE)); } else { new_port = HTTP_PORT; - if (use_ssl) { + if (config.use_ssl) { new_port = HTTPS_PORT; } } if (new_port > MAX_PORT) { - die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s%s\n"), MAX_PORT, location, display_html ? "" : ""); + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s%s\n"), MAX_PORT, location, + config.display_html ? "" : ""); } /* by RFC 7231 relative URLs in Location should be taken relative to * the original URL, so we try to form a new absolute URL here */ + char *new_host; if (!uri.scheme.first && !uri.hostText.first) { - new_host = strdup(host_name ? host_name : server_address); - new_port = server_port; - if (use_ssl) { + new_host = strdup(config.host_name ? config.host_name : config.server_address); + new_port = config.server_port; + if (config.use_ssl) { uri_string(uri.scheme, "https", DEFAULT_BUFFER_SIZE); } } else { @@ -1311,7 +1254,7 @@ void redir(curlhelp_write_curlbuf *header_buf) { /* compose new path */ /* TODO: handle fragments and query part of URL */ - new_url = (char *)calloc(1, DEFAULT_BUFFER_SIZE); + char *new_url = (char *)calloc(1, DEFAULT_BUFFER_SIZE); if (uri.pathHead) { const UriPathSegmentA *p = uri.pathHead; for (; p; p = p->next) { @@ -1320,37 +1263,37 @@ void redir(curlhelp_write_curlbuf *header_buf) { } } - if (server_port == new_port && !strncmp(server_address, new_host, MAX_IPV4_HOSTLENGTH) && - (host_name && !strncmp(host_name, new_host, MAX_IPV4_HOSTLENGTH)) && !strcmp(server_url, new_url)) { - die(STATE_CRITICAL, _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), use_ssl ? "https" : "http", - new_host, new_port, new_url, (display_html ? "" : "")); + if (config.server_port == new_port && !strncmp(config.server_address, new_host, MAX_IPV4_HOSTLENGTH) && + (config.host_name && !strncmp(config.host_name, new_host, MAX_IPV4_HOSTLENGTH)) && !strcmp(config.server_url, new_url)) { + die(STATE_CRITICAL, _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), config.use_ssl ? "https" : "http", + new_host, new_port, new_url, (config.display_html ? "" : "")); } /* set new values for redirected request */ - if (!(followsticky & STICKY_HOST)) { - free(server_address); - server_address = strndup(new_host, MAX_IPV4_HOSTLENGTH); + if (!(config.followsticky & STICKY_HOST)) { + free(config.server_address); + config.server_address = strndup(new_host, MAX_IPV4_HOSTLENGTH); } - if (!(followsticky & STICKY_PORT)) { - server_port = (unsigned short)new_port; + if (!(config.followsticky & STICKY_PORT)) { + config.server_port = (unsigned short)new_port; } - free(host_name); - host_name = strndup(new_host, MAX_IPV4_HOSTLENGTH); + free(config.host_name); + config.host_name = strndup(new_host, MAX_IPV4_HOSTLENGTH); /* reset virtual port */ - virtual_port = server_port; + config.virtual_port = config.server_port; free(new_host); - free(server_url); - server_url = new_url; + free(config.server_url); + config.server_url = new_url; uriFreeUriMembersA(&uri); if (verbose) { - printf(_("Redirection to %s://%s:%d%s\n"), use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, - server_url); + printf(_("Redirection to %s://%s:%d%s\n"), config.use_ssl ? "https" : "http", + config.host_name ? config.host_name : config.server_address, config.server_port, config.server_url); } /* TODO: the hash component MUST be taken from the original URL and @@ -1358,7 +1301,7 @@ void redir(curlhelp_write_curlbuf *header_buf) { */ cleanup(); - check_http(); + check_http(config); } /* check whether a file exists */ @@ -1369,11 +1312,7 @@ void test_file(char *path) { usage2(_("file does not exist or is not readable"), path); } -bool process_arguments(int argc, char **argv) { - char *p; - int c = 1; - char *temp; - +check_curl_config_wrapper process_arguments(int argc, char **argv) { enum { INVERT_REGEX = CHAR_MAX + 1, SNI_OPTION, @@ -1387,8 +1326,6 @@ bool process_arguments(int argc, char **argv) { STATE_REGEX }; - int option = 0; - int got_plus = 0; static struct option longopts[] = {STD_LONG_OPTS, {"link", no_argument, 0, 'L'}, {"nohtml", no_argument, 0, 'n'}, @@ -1434,38 +1371,48 @@ bool process_arguments(int argc, char **argv) { {"haproxy-protocol", no_argument, 0, HAPROXY_PROTOCOL}, {0, 0, 0, 0}}; + check_curl_config_wrapper result = { + .errorcode = OK, + .config = check_curl_config_init(), + }; + if (argc < 2) { - return false; + result.errorcode = ERROR; + return result; } /* support check_http compatible arguments */ - for (c = 1; c < argc; c++) { - if (strcmp("-to", argv[c]) == 0) { - strcpy(argv[c], "-t"); + for (int index = 1; index < argc; index++) { + if (strcmp("-to", argv[index]) == 0) { + strcpy(argv[index], "-t"); } - if (strcmp("-hn", argv[c]) == 0) { - strcpy(argv[c], "-H"); + if (strcmp("-hn", argv[index]) == 0) { + strcpy(argv[index], "-H"); } - if (strcmp("-wt", argv[c]) == 0) { - strcpy(argv[c], "-w"); + if (strcmp("-wt", argv[index]) == 0) { + strcpy(argv[index], "-w"); } - if (strcmp("-ct", argv[c]) == 0) { - strcpy(argv[c], "-c"); + if (strcmp("-ct", argv[index]) == 0) { + strcpy(argv[index], "-c"); } - if (strcmp("-nohtml", argv[c]) == 0) { - strcpy(argv[c], "-n"); + if (strcmp("-nohtml", argv[index]) == 0) { + strcpy(argv[index], "-n"); } } - server_url = strdup(DEFAULT_SERVER_URL); - - while (1) { - c = getopt_long(argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:DnlLS::m:M:NEB", longopts, &option); - if (c == -1 || c == EOF || c == 1) { + int option = 0; + char *warning_thresholds = NULL; + char *critical_thresholds = NULL; + int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE; + bool specify_port = false; + + while (true) { + int option_index = getopt_long(argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:DnlLS::m:M:NEB", longopts, &option); + if (option_index == -1 || option_index == EOF || option_index == 1) { break; } - switch (c) { + switch (option_index) { case 'h': print_help(); exit(STATE_UNKNOWN); @@ -1482,7 +1429,7 @@ bool process_arguments(int argc, char **argv) { if (!is_intnonneg(optarg)) { usage2(_("Timeout interval must be a positive integer"), optarg); } else { - socket_timeout = (int)strtol(optarg, NULL, 10); + result.config.socket_timeout = (int)strtol(optarg, NULL, 10); } break; case 'c': /* critical time threshold */ @@ -1492,28 +1439,30 @@ bool process_arguments(int argc, char **argv) { warning_thresholds = optarg; break; case 'H': /* virtual host */ - host_name = strdup(optarg); - if (host_name[0] == '[') { - if ((p = strstr(host_name, "]:")) != NULL) { /* [IPv6]:port */ - virtual_port = atoi(p + 2); + result.config.host_name = strdup(optarg); + char *p; + int host_name_length; + if (result.config.host_name[0] == '[') { + if ((p = strstr(result.config.host_name, "]:")) != NULL) { /* [IPv6]:port */ + result.config.virtual_port = atoi(p + 2); /* cut off the port */ - host_name_length = strlen(host_name) - strlen(p) - 1; - free(host_name); - host_name = strndup(optarg, host_name_length); + host_name_length = strlen(result.config.host_name) - strlen(p) - 1; + free(result.config.host_name); + result.config.host_name = strndup(optarg, host_name_length); } - } else if ((p = strchr(host_name, ':')) != NULL && strchr(++p, ':') == NULL) { /* IPv4:port or host:port */ - virtual_port = atoi(p); + } else if ((p = strchr(result.config.host_name, ':')) != NULL && strchr(++p, ':') == NULL) { /* IPv4:port or host:port */ + result.config.virtual_port = atoi(p); /* cut off the port */ - host_name_length = strlen(host_name) - strlen(p) - 1; - free(host_name); - host_name = strndup(optarg, host_name_length); + host_name_length = strlen(result.config.host_name) - strlen(p) - 1; + free(result.config.host_name); + result.config.host_name = strndup(optarg, host_name_length); } break; case 'I': /* internet address */ - server_address = strdup(optarg); + result.config.server_address = strdup(optarg); break; case 'u': /* URL path */ - server_url = strdup(optarg); + result.config.server_url = strdup(optarg); break; case 'p': /* Server port */ if (!is_intnonneg(optarg)) { @@ -1522,144 +1471,148 @@ bool process_arguments(int argc, char **argv) { if (strtol(optarg, NULL, 10) > MAX_PORT) { usage2(_("Invalid port number, supplied port number is too big"), optarg); } - server_port = (unsigned short)strtol(optarg, NULL, 10); + result.config.server_port = (unsigned short)strtol(optarg, NULL, 10); specify_port = true; } break; case 'a': /* authorization info */ - strncpy(user_auth, optarg, MAX_INPUT_BUFFER - 1); - user_auth[MAX_INPUT_BUFFER - 1] = 0; + strncpy(result.config.user_auth, optarg, MAX_INPUT_BUFFER - 1); + result.config.user_auth[MAX_INPUT_BUFFER - 1] = 0; break; case 'b': /* proxy-authorization info */ - strncpy(proxy_auth, optarg, MAX_INPUT_BUFFER - 1); - proxy_auth[MAX_INPUT_BUFFER - 1] = 0; + strncpy(result.config.proxy_auth, optarg, MAX_INPUT_BUFFER - 1); + result.config.proxy_auth[MAX_INPUT_BUFFER - 1] = 0; break; case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */ - if (!http_post_data) { - http_post_data = strdup(optarg); + if (!result.config.http_post_data) { + result.config.http_post_data = strdup(optarg); } - if (!http_method) { - http_method = strdup("POST"); + if (!result.config.http_method) { + result.config.http_method = strdup("POST"); } break; case 'j': /* Set HTTP method */ - if (http_method) { - free(http_method); + if (result.config.http_method) { + free(result.config.http_method); } - http_method = strdup(optarg); + result.config.http_method = strdup(optarg); break; case 'A': /* useragent */ - strncpy(user_agent, optarg, DEFAULT_BUFFER_SIZE); - user_agent[DEFAULT_BUFFER_SIZE - 1] = '\0'; + strncpy(result.config.user_agent, optarg, DEFAULT_BUFFER_SIZE); + result.config.user_agent[DEFAULT_BUFFER_SIZE - 1] = '\0'; break; case 'k': /* Additional headers */ - if (http_opt_headers_count == 0) { - http_opt_headers = malloc(sizeof(char *) * (++http_opt_headers_count)); + if (result.config.http_opt_headers_count == 0) { + result.config.http_opt_headers = malloc(sizeof(char *) * (++result.config.http_opt_headers_count)); } else { - http_opt_headers = realloc(http_opt_headers, sizeof(char *) * (++http_opt_headers_count)); + result.config.http_opt_headers = + realloc(result.config.http_opt_headers, sizeof(char *) * (++result.config.http_opt_headers_count)); } - http_opt_headers[http_opt_headers_count - 1] = optarg; + result.config.http_opt_headers[result.config.http_opt_headers_count - 1] = optarg; break; case 'L': /* show html link */ - display_html = true; + result.config.display_html = true; break; case 'n': /* do not show html link */ - display_html = false; + result.config.display_html = false; break; case 'C': /* Check SSL cert validity */ #ifdef LIBCURL_FEATURE_SSL + char *temp; if ((temp = strchr(optarg, ',')) != NULL) { *temp = '\0'; if (!is_intnonneg(optarg)) { usage2(_("Invalid certificate expiration period"), optarg); } - days_till_exp_warn = atoi(optarg); + result.config.days_till_exp_warn = atoi(optarg); *temp = ','; temp++; if (!is_intnonneg(temp)) { usage2(_("Invalid certificate expiration period"), temp); } - days_till_exp_crit = atoi(temp); + result.config.days_till_exp_crit = atoi(temp); } else { - days_till_exp_crit = 0; + result.config.days_till_exp_crit = 0; if (!is_intnonneg(optarg)) { usage2(_("Invalid certificate expiration period"), optarg); } - days_till_exp_warn = atoi(optarg); + result.config.days_till_exp_warn = atoi(optarg); } - check_cert = true; + result.config.check_cert = true; goto enable_ssl; #endif case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */ #ifdef HAVE_SSL - continue_after_check_cert = true; + result.config.continue_after_check_cert = true; break; #endif case 'J': /* use client certificate */ #ifdef LIBCURL_FEATURE_SSL test_file(optarg); - client_cert = optarg; + result.config.client_cert = optarg; goto enable_ssl; #endif case 'K': /* use client private key */ #ifdef LIBCURL_FEATURE_SSL test_file(optarg); - client_privkey = optarg; + result.config.client_privkey = optarg; goto enable_ssl; #endif #ifdef LIBCURL_FEATURE_SSL case CA_CERT_OPTION: /* use CA chain file */ test_file(optarg); - ca_cert = optarg; + result.config.ca_cert = optarg; goto enable_ssl; #endif #ifdef LIBCURL_FEATURE_SSL case 'D': /* verify peer certificate & host */ - verify_peer_and_host = true; + result.config.verify_peer_and_host = true; break; #endif case 'S': /* use SSL */ #ifdef LIBCURL_FEATURE_SSL + { enable_ssl: - use_ssl = true; + bool got_plus = false; + result.config.use_ssl = true; /* ssl_version initialized to CURL_SSLVERSION_DEFAULT as a default. * Only set if it's non-zero. This helps when we include multiple * parameters, like -S and -C combinations */ - ssl_version = CURL_SSLVERSION_DEFAULT; - if (c == 'S' && optarg != NULL) { + result.config.ssl_version = CURL_SSLVERSION_DEFAULT; + if (option_index == 'S' && optarg != NULL) { char *plus_ptr = strchr(optarg, '+'); if (plus_ptr) { - got_plus = 1; + got_plus = true; *plus_ptr = '\0'; } if (optarg[0] == '2') { - ssl_version = CURL_SSLVERSION_SSLv2; + result.config.ssl_version = CURL_SSLVERSION_SSLv2; } else if (optarg[0] == '3') { - ssl_version = CURL_SSLVERSION_SSLv3; + result.config.ssl_version = CURL_SSLVERSION_SSLv3; } else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0")) # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) - ssl_version = CURL_SSLVERSION_TLSv1_0; + result.config.ssl_version = CURL_SSLVERSION_TLSv1_0; # else - ssl_version = CURL_SSLVERSION_DEFAULT; + result.config.ssl_version = CURL_SSLVERSION_DEFAULT; # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ else if (!strcmp(optarg, "1.1")) # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) - ssl_version = CURL_SSLVERSION_TLSv1_1; + result.config.ssl_version = CURL_SSLVERSION_TLSv1_1; # else - ssl_version = CURL_SSLVERSION_DEFAULT; + result.config.ssl_version = CURL_SSLVERSION_DEFAULT; # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ else if (!strcmp(optarg, "1.2")) # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) - ssl_version = CURL_SSLVERSION_TLSv1_2; + result.config.ssl_version = CURL_SSLVERSION_TLSv1_2; # else - ssl_version = CURL_SSLVERSION_DEFAULT; + result.config.ssl_version = CURL_SSLVERSION_DEFAULT; # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ else if (!strcmp(optarg, "1.3")) # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) - ssl_version = CURL_SSLVERSION_TLSv1_3; + result.config.ssl_version = CURL_SSLVERSION_TLSv1_3; # else - ssl_version = CURL_SSLVERSION_DEFAULT; + result.config.ssl_version = CURL_SSLVERSION_DEFAULT; # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */ else { usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 (with optional '+' suffix)")); @@ -1667,40 +1620,40 @@ bool process_arguments(int argc, char **argv) { } # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) if (got_plus) { - switch (ssl_version) { + switch (result.config.ssl_version) { case CURL_SSLVERSION_TLSv1_3: - ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; + result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; break; case CURL_SSLVERSION_TLSv1_2: case CURL_SSLVERSION_TLSv1_1: case CURL_SSLVERSION_TLSv1_0: - ssl_version |= CURL_SSLVERSION_MAX_DEFAULT; + result.config.ssl_version |= CURL_SSLVERSION_MAX_DEFAULT; break; } } else { - switch (ssl_version) { + switch (result.config.ssl_version) { case CURL_SSLVERSION_TLSv1_3: - ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; + result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; break; case CURL_SSLVERSION_TLSv1_2: - ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2; + result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2; break; case CURL_SSLVERSION_TLSv1_1: - ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1; + result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1; break; case CURL_SSLVERSION_TLSv1_0: - ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0; + result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0; break; } } # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */ if (verbose >= 2) { - printf(_("* Set SSL/TLS version to %d\n"), ssl_version); + printf(_("* Set SSL/TLS version to %d\n"), result.config.ssl_version); } if (!specify_port) { - server_port = HTTPS_PORT; + result.config.server_port = HTTPS_PORT; } - break; + } break; #else /* LIBCURL_FEATURE_SSL */ /* -C -J and -K fall through to here without SSL */ usage4(_("Invalid option - SSL is not available")); @@ -1713,50 +1666,53 @@ bool process_arguments(int argc, char **argv) { if (!is_intnonneg(optarg)) { usage2(_("Invalid max_redirs count"), optarg); } else { - max_depth = atoi(optarg); + result.config.max_depth = atoi(optarg); } break; case 'f': /* onredirect */ if (!strcmp(optarg, "ok")) { - onredirect = STATE_OK; + result.config.onredirect = STATE_OK; } else if (!strcmp(optarg, "warning")) { - onredirect = STATE_WARNING; + result.config.onredirect = STATE_WARNING; } else if (!strcmp(optarg, "critical")) { - onredirect = STATE_CRITICAL; + result.config.onredirect = STATE_CRITICAL; } else if (!strcmp(optarg, "unknown")) { - onredirect = STATE_UNKNOWN; + result.config.onredirect = STATE_UNKNOWN; } else if (!strcmp(optarg, "follow")) { - onredirect = STATE_DEPENDENT; + result.config.onredirect = STATE_DEPENDENT; } else if (!strcmp(optarg, "stickyport")) { - onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST | STICKY_PORT; + result.config.onredirect = STATE_DEPENDENT, result.config.followmethod = FOLLOW_HTTP_CURL, + result.config.followsticky = STICKY_HOST | STICKY_PORT; } else if (!strcmp(optarg, "sticky")) { - onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST; + result.config.onredirect = STATE_DEPENDENT, result.config.followmethod = FOLLOW_HTTP_CURL, + result.config.followsticky = STICKY_HOST; } else if (!strcmp(optarg, "follow")) { - onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_NONE; + result.config.onredirect = STATE_DEPENDENT, result.config.followmethod = FOLLOW_HTTP_CURL, + result.config.followsticky = STICKY_NONE; } else if (!strcmp(optarg, "curl")) { - onredirect = STATE_DEPENDENT, followmethod = FOLLOW_LIBCURL; + result.config.onredirect = STATE_DEPENDENT, result.config.followmethod = FOLLOW_LIBCURL; } else { usage2(_("Invalid onredirect option"), optarg); } if (verbose >= 2) { - printf(_("* Following redirects set to %s\n"), state_text(onredirect)); + printf(_("* Following redirects set to %s\n"), state_text(result.config.onredirect)); } break; case 'd': /* string or substring */ - strncpy(header_expect, optarg, MAX_INPUT_BUFFER - 1); - header_expect[MAX_INPUT_BUFFER - 1] = 0; + strncpy(result.config.header_expect, optarg, MAX_INPUT_BUFFER - 1); + result.config.header_expect[MAX_INPUT_BUFFER - 1] = 0; break; case 's': /* string or substring */ - strncpy(string_expect, optarg, MAX_INPUT_BUFFER - 1); - string_expect[MAX_INPUT_BUFFER - 1] = 0; + strncpy(result.config.string_expect, optarg, MAX_INPUT_BUFFER - 1); + result.config.string_expect[MAX_INPUT_BUFFER - 1] = 0; break; case 'e': /* string or substring */ - strncpy(server_expect, optarg, MAX_INPUT_BUFFER - 1); - server_expect[MAX_INPUT_BUFFER - 1] = 0; - server_expect_yn = 1; + strncpy(result.config.server_expect, optarg, MAX_INPUT_BUFFER - 1); + result.config.server_expect[MAX_INPUT_BUFFER - 1] = 0; + result.config.server_expect_yn = true; break; case 'T': /* Content-type */ - http_content_type = strdup(optarg); + result.config.http_content_type = strdup(optarg); break; case 'l': /* linespan */ cflags &= ~REG_NEWLINE; @@ -1765,23 +1721,25 @@ bool process_arguments(int argc, char **argv) { cflags |= REG_ICASE; // fall through case 'r': /* regex */ - strncpy(regexp, optarg, MAX_RE_SIZE - 1); - regexp[MAX_RE_SIZE - 1] = 0; - errcode = regcomp(&preg, regexp, cflags); + strncpy(result.config.regexp, optarg, MAX_RE_SIZE - 1); + result.config.regexp[MAX_RE_SIZE - 1] = 0; + regex_t preg; + int errcode = regcomp(&preg, result.config.regexp, cflags); if (errcode != 0) { (void)regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); printf(_("Could Not Compile Regular Expression: %s"), errbuf); - return false; + result.errorcode = ERROR; + return result; } break; case INVERT_REGEX: - invert_regex = true; + result.config.invert_regex = true; break; case STATE_REGEX: if (!strcasecmp(optarg, "critical")) { - state_regex = STATE_CRITICAL; + result.config.state_regex = STATE_CRITICAL; } else if (!strcasecmp(optarg, "warning")) { - state_regex = STATE_WARNING; + result.config.state_regex = STATE_WARNING; } else { usage2(_("Invalid state-regex option"), optarg); } @@ -1806,7 +1764,7 @@ bool process_arguments(int argc, char **argv) { printf("Bad format: try \"-m min:max\"\n"); exit(STATE_WARNING); } else { - min_page_len = atoi(tmp); + result.config.min_page_len = atoi(tmp); } tmp = strtok(NULL, ":"); @@ -1814,52 +1772,52 @@ bool process_arguments(int argc, char **argv) { printf("Bad format: try \"-m min:max\"\n"); exit(STATE_WARNING); } else { - max_page_len = atoi(tmp); + result.config.max_page_len = atoi(tmp); } } else { - min_page_len = atoi(optarg); + result.config.min_page_len = atoi(optarg); } break; } case 'N': /* no-body */ - no_body = true; + result.config.no_body = true; break; case 'M': /* max-age */ { - int L = strlen(optarg); - if (L && optarg[L - 1] == 'm') { - maximum_age = atoi(optarg) * 60; - } else if (L && optarg[L - 1] == 'h') { - maximum_age = atoi(optarg) * 60 * 60; - } else if (L && optarg[L - 1] == 'd') { - maximum_age = atoi(optarg) * 60 * 60 * 24; - } else if (L && (optarg[L - 1] == 's' || isdigit(optarg[L - 1]))) { - maximum_age = atoi(optarg); + size_t option_length = strlen(optarg); + if (option_length && optarg[option_length - 1] == 'm') { + result.config.maximum_age = atoi(optarg) * 60; + } else if (option_length && optarg[option_length - 1] == 'h') { + result.config.maximum_age = atoi(optarg) * 60 * 60; + } else if (option_length && optarg[option_length - 1] == 'd') { + result.config.maximum_age = atoi(optarg) * 60 * 60 * 24; + } else if (option_length && (optarg[option_length - 1] == 's' || isdigit(optarg[option_length - 1]))) { + result.config.maximum_age = atoi(optarg); } else { fprintf(stderr, "unparsable max-age: %s\n", optarg); exit(STATE_WARNING); } if (verbose >= 2) { - printf("* Maximal age of document set to %d seconds\n", maximum_age); + printf("* Maximal age of document set to %d seconds\n", result.config.maximum_age); } } break; case 'E': /* show extended perfdata */ - show_extended_perfdata = true; + result.config.show_extended_perfdata = true; break; case 'B': /* print body content after status line */ - show_body = true; + result.config.show_body = true; break; case HTTP_VERSION_OPTION: - curl_http_version = CURL_HTTP_VERSION_NONE; + result.config.curl_http_version = CURL_HTTP_VERSION_NONE; if (strcmp(optarg, "1.0") == 0) { - curl_http_version = CURL_HTTP_VERSION_1_0; + result.config.curl_http_version = CURL_HTTP_VERSION_1_0; } else if (strcmp(optarg, "1.1") == 0) { - curl_http_version = CURL_HTTP_VERSION_1_1; + result.config.curl_http_version = CURL_HTTP_VERSION_1_1; } else if ((strcmp(optarg, "2.0") == 0) || (strcmp(optarg, "2") == 0)) { #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) - curl_http_version = CURL_HTTP_VERSION_2_0; + result.config.curl_http_version = CURL_HTTP_VERSION_2_0; #else - curl_http_version = CURL_HTTP_VERSION_NONE; + result.config.curl_http_version = CURL_HTTP_VERSION_NONE; #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) */ } else { fprintf(stderr, "unknown http-version parameter: %s\n", optarg); @@ -1867,13 +1825,13 @@ bool process_arguments(int argc, char **argv) { } break; case AUTOMATIC_DECOMPRESSION: - automatic_decompression = true; + result.config.automatic_decompression = true; break; case COOKIE_JAR: - cookie_jar_file = optarg; + result.config.cookie_jar_file = optarg; break; case HAPROXY_PROTOCOL: - haproxy_protocol = true; + result.config.haproxy_protocol = true; break; case '?': /* print short usage statement if args not parsable */ @@ -1882,82 +1840,82 @@ bool process_arguments(int argc, char **argv) { } } - c = optind; + int c = optind; - if (server_address == NULL && c < argc) { - server_address = strdup(argv[c++]); + if (result.config.server_address == NULL && c < argc) { + result.config.server_address = strdup(argv[c++]); } - if (host_name == NULL && c < argc) { - host_name = strdup(argv[c++]); + if (result.config.host_name == NULL && c < argc) { + result.config.host_name = strdup(argv[c++]); } - if (server_address == NULL) { - if (host_name == NULL) { + if (result.config.server_address == NULL) { + if (result.config.host_name == NULL) { usage4(_("You must specify a server address or host name")); } else { - server_address = strdup(host_name); + result.config.server_address = strdup(result.config.host_name); } } - set_thresholds(&thlds, warning_thresholds, critical_thresholds); + set_thresholds(&result.config.thlds, warning_thresholds, critical_thresholds); - if (critical_thresholds && thlds->critical->end > (double)socket_timeout) { - socket_timeout = (int)thlds->critical->end + 1; + if (critical_thresholds && result.config.thlds->critical->end > (double)result.config.socket_timeout) { + result.config.socket_timeout = (int)result.config.thlds->critical->end + 1; } if (verbose >= 2) { - printf("* Socket timeout set to %ld seconds\n", socket_timeout); + printf("* Socket timeout set to %ld seconds\n", result.config.socket_timeout); } - if (http_method == NULL) { - http_method = strdup("GET"); + if (result.config.http_method == NULL) { + result.config.http_method = strdup("GET"); } - if (client_cert && !client_privkey) { + if (result.config.client_cert && !result.config.client_privkey) { usage4(_("If you use a client certificate you must also specify a private key file")); } - if (virtual_port == 0) { - virtual_port = server_port; + if (result.config.virtual_port == 0) { + result.config.virtual_port = result.config.server_port; } else { - if ((use_ssl && server_port == HTTPS_PORT) || (!use_ssl && server_port == HTTP_PORT)) { + if ((result.config.use_ssl && result.config.server_port == HTTPS_PORT) || + (!result.config.use_ssl && result.config.server_port == HTTP_PORT)) { if (!specify_port) { - server_port = virtual_port; + result.config.server_port = result.config.virtual_port; } } } - return true; + return result; } -char *perfd_time(double elapsed_time) { - return fperfdata("time", elapsed_time, "s", thlds->warning ? true : false, thlds->warning ? thlds->warning->end : 0, - thlds->critical ? true : false, thlds->critical ? thlds->critical->end : 0, true, 0, true, socket_timeout); +char *perfd_time(double elapsed_time, thresholds *thlds, long socket_timeout) { + return fperfdata("time", elapsed_time, "s", thlds->warning, thlds->warning ? thlds->warning->end : 0, thlds->critical, + thlds->critical ? thlds->critical->end : 0, true, 0, true, socket_timeout); } -char *perfd_time_connect(double elapsed_time_connect) { +char *perfd_time_connect(double elapsed_time_connect, long socket_timeout) { return fperfdata("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, socket_timeout); } -char *perfd_time_ssl(double elapsed_time_ssl) { +char *perfd_time_ssl(double elapsed_time_ssl, long socket_timeout) { return fperfdata("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, socket_timeout); } -char *perfd_time_headers(double elapsed_time_headers) { +char *perfd_time_headers(double elapsed_time_headers, long socket_timeout) { return fperfdata("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, socket_timeout); } -char *perfd_time_firstbyte(double elapsed_time_firstbyte) { +char *perfd_time_firstbyte(double elapsed_time_firstbyte, long socket_timeout) { return fperfdata("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, true, socket_timeout); } -char *perfd_time_transfer(double elapsed_time_transfer) { +char *perfd_time_transfer(double elapsed_time_transfer, long socket_timeout) { return fperfdata("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, true, socket_timeout); } -char *perfd_size(int page_len) { - return perfdata("size", page_len, "B", (min_page_len > 0 ? true : false), min_page_len, (min_page_len > 0 ? true : false), 0, true, 0, - false, 0); +char *perfd_size(int page_len, int min_page_len) { + return perfdata("size", page_len, "B", (min_page_len > 0), min_page_len, (min_page_len > 0), 0, true, 0, false, 0); } void print_help(void) { @@ -2259,11 +2217,6 @@ void curlhelp_freereadbuffer(curlhelp_read_curlbuf *buf) { /* TODO: where to put this, it's actually part of sstrings2 (logically)? */ const char *strrstr2(const char *haystack, const char *needle) { - int counter; - size_t len; - const char *prev_pos; - const char *pos; - if (haystack == NULL || needle == NULL) { return NULL; } @@ -2272,10 +2225,10 @@ const char *strrstr2(const char *haystack, const char *needle) { return NULL; } - counter = 0; - prev_pos = NULL; - pos = haystack; - len = strlen(needle); + int counter = 0; + const char *prev_pos = NULL; + const char *pos = haystack; + size_t len = strlen(needle); for (;;) { pos = strstr(pos, needle); if (pos == NULL) { @@ -2294,37 +2247,29 @@ const char *strrstr2(const char *haystack, const char *needle) { } int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) { - char *first_line_end; - char *p; - size_t first_line_len; - char *pp; - const char *start; - char *first_line_buf; - /* find last start of a new header */ - start = strrstr2(buf, "\r\nHTTP/"); + const char *start = strrstr2(buf, "\r\nHTTP/"); if (start != NULL) { start += 2; buf = start; } - first_line_end = strstr(buf, "\r\n"); + char *first_line_end = strstr(buf, "\r\n"); if (first_line_end == NULL) { return -1; } - first_line_len = (size_t)(first_line_end - buf); + size_t first_line_len = (size_t)(first_line_end - buf); status_line->first_line = (char *)malloc(first_line_len + 1); if (status_line->first_line == NULL) { return -1; } memcpy(status_line->first_line, buf, first_line_len); status_line->first_line[first_line_len] = '\0'; - first_line_buf = strdup(status_line->first_line); + char *first_line_buf = strdup(status_line->first_line); /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */ - - p = strtok(first_line_buf, "/"); + char *p = strtok(first_line_buf, "/"); if (p == NULL) { free(first_line_buf); return -1; @@ -2339,6 +2284,8 @@ int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) free(first_line_buf); return -1; } + + char *pp; if (strchr(p, '.') != NULL) { /* HTTP 1.x case */ @@ -2418,15 +2365,11 @@ char *get_header_value(const struct phr_header *headers, const size_t nof_header return NULL; } -int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[DEFAULT_BUFFER_SIZE]) { - char *server_date = NULL; - char *document_date = NULL; - int date_result = STATE_OK; - curlhelp_statusline status_line; +int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[DEFAULT_BUFFER_SIZE], int maximum_age) { struct phr_header headers[255]; size_t nof_headers = 255; + curlhelp_statusline status_line; size_t msglen; - int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor, &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0); @@ -2434,9 +2377,10 @@ int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[D die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); } - server_date = get_header_value(headers, nof_headers, "date"); - document_date = get_header_value(headers, nof_headers, "last-modified"); + char *server_date = get_header_value(headers, nof_headers, "date"); + char *document_date = get_header_value(headers, nof_headers, "last-modified"); + int date_result = STATE_OK; if (!server_date || !*server_date) { char tmp[DEFAULT_BUFFER_SIZE]; @@ -2511,13 +2455,10 @@ int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[D } int get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_write_curlbuf *body_buf) { - size_t content_length = 0; struct phr_header headers[255]; size_t nof_headers = 255; size_t msglen; - char *content_length_s = NULL; curlhelp_statusline status_line; - int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor, &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0); @@ -2525,12 +2466,13 @@ int get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_ die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); } - content_length_s = get_header_value(headers, nof_headers, "content-length"); + char *content_length_s = get_header_value(headers, nof_headers, "content-length"); if (!content_length_s) { return header_buf->buflen + body_buf->buflen; } + content_length_s += strspn(content_length_s, " \t"); - content_length = atoi(content_length_s); + size_t content_length = atoi(content_length_s); if (content_length != body_buf->buflen) { /* TODO: should we warn if the actual and the reported body length don't match? */ } @@ -2544,22 +2486,19 @@ int get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_ /* TODO: is there a better way in libcurl to check for the SSL library? */ curlhelp_ssl_library curlhelp_get_ssl_library(void) { - curl_version_info_data *version_data; - char *ssl_version; - char *library; curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN; - version_data = curl_version_info(CURLVERSION_NOW); + curl_version_info_data *version_data = curl_version_info(CURLVERSION_NOW); if (version_data == NULL) { return CURLHELP_SSL_LIBRARY_UNKNOWN; } - ssl_version = strdup(version_data->ssl_version); + char *ssl_version = strdup(version_data->ssl_version); if (ssl_version == NULL) { return CURLHELP_SSL_LIBRARY_UNKNOWN; } - library = strtok(ssl_version, "/"); + char *library = strtok(ssl_version, "/"); if (library == NULL) { return CURLHELP_SSL_LIBRARY_UNKNOWN; } @@ -2602,21 +2541,18 @@ const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library ssl_library) { #ifdef LIBCURL_FEATURE_SSL # ifndef USE_OPENSSL time_t parse_cert_date(const char *s) { - struct tm tm; - time_t date; - char *res; - if (!s) { return -1; } /* Jan 17 14:25:12 2020 GMT */ - res = strptime(s, "%Y-%m-%d %H:%M:%S GMT", &tm); + struct tm tm; + char *res = strptime(s, "%Y-%m-%d %H:%M:%S GMT", &tm); /* Sep 11 12:00:00 2020 GMT */ if (res == NULL) { strptime(s, "%Y %m %d %H:%M:%S GMT", &tm); } - date = mktime(&tm); + time_t date = mktime(&tm); return date; } @@ -2625,7 +2561,6 @@ time_t parse_cert_date(const char *s) { * OpenSSL could be this function */ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_warn, int days_till_exp_crit) { - int i; struct curl_slist *slist; int cname_found = 0; char *start_date_str = NULL; @@ -2643,7 +2578,7 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_ printf("**** REQUEST CERTIFICATES ****\n"); } - for (i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) { + for (int i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) { for (slist = cert_ptr->to_certinfo->certinfo[i]; slist; slist = slist->next) { /* find first common name in subject, * TODO: check alternative subjects for diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h new file mode 100644 index 00000000..9085fbbb --- /dev/null +++ b/plugins/check_curl.d/config.h @@ -0,0 +1,141 @@ +#pragma once + +#include "../../config.h" +#include "common.h" +#include "states.h" +#include "thresholds.h" +#include +#include +#include "curl/curl.h" + +enum { + MAX_RE_SIZE = 1024, + HTTP_PORT = 80, + HTTPS_PORT = 443, + MAX_PORT = 65535, + DEFAULT_MAX_REDIRS = 15 +}; + +enum { + FOLLOW_HTTP_CURL = 0, + FOLLOW_LIBCURL = 1 +}; + +enum { + STICKY_NONE = 0, + STICKY_HOST = 1, + STICKY_PORT = 2 +}; + +#define HTTP_EXPECT "HTTP/" +#define DEFAULT_BUFFER_SIZE 2048 +#define DEFAULT_SERVER_URL "/" + +typedef struct { + char *server_address; + unsigned short server_port; + unsigned short virtual_port; + char *host_name; + char *server_url; + + bool automatic_decompression; + bool haproxy_protocol; + char *client_cert; + char *client_privkey; + char *ca_cert; + bool use_ssl; + int ssl_version; + char *http_method; + char user_agent[DEFAULT_BUFFER_SIZE]; + char **http_opt_headers; + int http_opt_headers_count; + char *http_post_data; + int max_depth; + char *http_content_type; + long socket_timeout; + char user_auth[MAX_INPUT_BUFFER]; + char proxy_auth[MAX_INPUT_BUFFER]; + int followmethod; + int followsticky; + bool no_body; + int curl_http_version; + char *cookie_jar_file; + + int maximum_age; + char regexp[MAX_RE_SIZE]; + int state_regex; + bool invert_regex; + bool verify_peer_and_host; + bool check_cert; + bool continue_after_check_cert; + int days_till_exp_warn; + int days_till_exp_crit; + thresholds *thlds; + int min_page_len; + int max_page_len; + char server_expect[MAX_INPUT_BUFFER]; + bool server_expect_yn; + char string_expect[MAX_INPUT_BUFFER]; + char header_expect[MAX_INPUT_BUFFER]; + int onredirect; + + bool show_extended_perfdata; + bool show_body; + bool display_html; +} check_curl_config; + +check_curl_config check_curl_config_init() { + check_curl_config tmp = { + .server_address = NULL, + .server_port = HTTP_PORT, + .virtual_port = 0, + .host_name = NULL, + .server_url = strdup(DEFAULT_SERVER_URL), + + .automatic_decompression = false, + .haproxy_protocol = false, + .client_cert = NULL, + .client_privkey = NULL, + .ca_cert = NULL, + .use_ssl = false, + .ssl_version = CURL_SSLVERSION_DEFAULT, + .http_method = NULL, + .user_agent = {}, + .http_opt_headers = NULL, + .http_opt_headers_count = 0, + .http_post_data = NULL, + .max_depth = DEFAULT_MAX_REDIRS, + .http_content_type = NULL, + .socket_timeout = DEFAULT_SOCKET_TIMEOUT, + .user_auth = "", + .proxy_auth = "", + .followmethod = FOLLOW_HTTP_CURL, + .followsticky = STICKY_NONE, + .no_body = false, + .curl_http_version = CURL_HTTP_VERSION_NONE, + .cookie_jar_file = NULL, + + .maximum_age = -1, + .regexp = {}, + .state_regex = STATE_CRITICAL, + .invert_regex = false, + .verify_peer_and_host = false, + .check_cert = false, + .continue_after_check_cert = false, + .days_till_exp_warn = 0, + .days_till_exp_crit = 0, + .thlds = NULL, + .min_page_len = 0, + .max_page_len = 0, + .server_expect = HTTP_EXPECT, + .server_expect_yn = false, + .string_expect = "", + .header_expect = "", + .onredirect = STATE_OK, + + .show_extended_perfdata = false, + .show_body = false, + .display_html = false, + }; + return tmp; +} -- cgit v1.2.3-74-g34f1 From e855107eeb882d25ce777562d16886ea9aa2633e Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 6 Jul 2025 23:46:16 +0200 Subject: check_curl: clang-format --- plugins/check_curl.c | 771 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 495 insertions(+), 276 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 4806bf14..5b8a06be 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -169,10 +169,13 @@ static void print_help(void); void print_usage(void); static void print_curl_version(void); static int curlhelp_initwritebuffer(curlhelp_write_curlbuf * /*buf*/); -static size_t curlhelp_buffer_write_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, void * /*stream*/); +static size_t curlhelp_buffer_write_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, + void * /*stream*/); static void curlhelp_freewritebuffer(curlhelp_write_curlbuf * /*buf*/); -static int curlhelp_initreadbuffer(curlhelp_read_curlbuf * /*buf*/, const char * /*data*/, size_t /*datalen*/); -static size_t curlhelp_buffer_read_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, void * /*stream*/); +static int curlhelp_initreadbuffer(curlhelp_read_curlbuf * /*buf*/, const char * /*data*/, + size_t /*datalen*/); +static size_t curlhelp_buffer_read_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, + void * /*stream*/); static void curlhelp_freereadbuffer(curlhelp_read_curlbuf * /*buf*/); static curlhelp_ssl_library curlhelp_get_ssl_library(void); static const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library /*ssl_library*/); @@ -180,9 +183,12 @@ int net_noopenssl_check_certificate(cert_ptr_union *, int, int); static int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * /*status_line*/); static void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/); -static char *get_header_value(const struct phr_header *headers, size_t nof_headers, const char *header); -static int check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, char (*msg)[DEFAULT_BUFFER_SIZE], int /*maximum_age*/); -static int get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_write_curlbuf *body_buf); +static char *get_header_value(const struct phr_header *headers, size_t nof_headers, + const char *header); +static int check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, + char (*msg)[DEFAULT_BUFFER_SIZE], int /*maximum_age*/); +static int get_content_length(const curlhelp_write_curlbuf *header_buf, + const curlhelp_write_curlbuf *body_buf); #if defined(HAVE_SSL) && defined(USE_OPENSSL) int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit); @@ -208,14 +214,14 @@ int main(int argc, char **argv) { /* set defaults */ if (config.user_agent == NULL) { - snprintf(config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", progname, NP_VERSION, VERSION, - curl_version()); + snprintf(config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", + progname, NP_VERSION, VERSION, curl_version()); } if (config.display_html) { printf("", config.use_ssl ? "https" : "http", - config.host_name ? config.host_name : config.server_address, config.virtual_port ? config.virtual_port : config.server_port, - config.server_url); + config.host_name ? config.host_name : config.server_address, + config.virtual_port ? config.virtual_port : config.server_port, config.server_url); } exit(check_http(config)); @@ -313,7 +319,8 @@ static int expected_statuscode(const char *reply, const char *statuscodes) { void handle_curl_option_return_code(CURLcode res, const char *option) { if (res != CURLE_OK) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Error while setting cURL option '%s': cURL returned %d - %s"), option, res, + snprintf(msg, DEFAULT_BUFFER_SIZE, + _("Error while setting cURL option '%s': cURL returned %d - %s"), option, res, curl_easy_strerror(res)); die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } @@ -351,7 +358,8 @@ int lookup_host(const char *host, char *buf, size_t buflen) { inet_ntop(res->ai_family, ptr, addrstr, 100); if (verbose >= 1) { - printf("* getaddrinfo IPv%d address: %s\n", res->ai_family == PF_INET6 ? 6 : 4, addrstr); + printf("* getaddrinfo IPv%d address: %s\n", res->ai_family == PF_INET6 ? 6 : 4, + addrstr); } // Append all IPs to buf as a comma-separated string @@ -416,17 +424,21 @@ mp_state_enum check_http(check_curl_config config) { atexit(cleanup); if (verbose >= 1) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_VERBOSE, 1), "CURLOPT_VERBOSE"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_VERBOSE, 1), + "CURLOPT_VERBOSE"); } /* print everything on stdout like check_http would do */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_STDERR, stdout), "CURLOPT_STDERR"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_STDERR, stdout), + "CURLOPT_STDERR"); if (config.automatic_decompression) #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""), "CURLOPT_ACCEPT_ENCODING"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""), + "CURLOPT_ACCEPT_ENCODING"); #else - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ENCODING, ""), + "CURLOPT_ENCODING"); #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */ /* initialize buffer for body of the answer */ @@ -434,44 +446,57 @@ mp_state_enum check_http(check_curl_config config) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n"); } body_buf_initialized = true; - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback), - "CURLOPT_WRITEFUNCTION"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&body_buf), "CURLOPT_WRITEDATA"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, + (curl_write_callback)curlhelp_buffer_write_callback), + "CURLOPT_WRITEFUNCTION"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&body_buf), + "CURLOPT_WRITEDATA"); /* initialize buffer for header of the answer */ if (curlhelp_initwritebuffer(&header_buf) < 0) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n"); } header_buf_initialized = true; - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback), - "CURLOPT_HEADERFUNCTION"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&header_buf), "CURLOPT_WRITEHEADER"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, + (curl_write_callback)curlhelp_buffer_write_callback), + "CURLOPT_HEADERFUNCTION"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&header_buf), + "CURLOPT_WRITEHEADER"); /* set the error buffer */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf), "CURLOPT_ERRORBUFFER"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf), + "CURLOPT_ERRORBUFFER"); /* set timeouts */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, config.socket_timeout), "CURLOPT_CONNECTTIMEOUT"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_TIMEOUT, config.socket_timeout), "CURLOPT_TIMEOUT"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, config.socket_timeout), + "CURLOPT_CONNECTTIMEOUT"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_TIMEOUT, config.socket_timeout), + "CURLOPT_TIMEOUT"); /* enable haproxy protocol */ if (config.haproxy_protocol) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HAPROXYPROTOCOL, 1L), "CURLOPT_HAPROXYPROTOCOL"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HAPROXYPROTOCOL, 1L), + "CURLOPT_HAPROXYPROTOCOL"); } - // fill dns resolve cache to make curl connect to the given server_address instead of the host_name, only required for ssl, because we - // use the host_name later on to make SNI happy + // fill dns resolve cache to make curl connect to the given server_address instead of the + // host_name, only required for ssl, because we use the host_name later on to make SNI happy struct curl_slist *host = NULL; char dnscache[DEFAULT_BUFFER_SIZE]; char addrstr[DEFAULT_BUFFER_SIZE / 2]; if (config.use_ssl && config.host_name != NULL) { CURLcode res; if ((res = lookup_host(config.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2)) != 0) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), + snprintf(msg, DEFAULT_BUFFER_SIZE, + _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), config.server_address, res, gai_strerror(res)); die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } - snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", config.host_name, config.server_port, addrstr); + snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", config.host_name, config.server_port, + addrstr); host = curl_slist_append(NULL, dnscache); curl_easy_setopt(curl, CURLOPT_RESOLVE, host); if (verbose >= 1) { @@ -486,7 +511,8 @@ mp_state_enum check_http(check_curl_config config) { if (new_server_address == NULL) { die(STATE_UNKNOWN, "HTTP UNKNOWN - Unable to allocate memory\n"); } - snprintf(new_server_address, strlen(config.server_address) + 3, "[%s]", config.server_address); + snprintf(new_server_address, strlen(config.server_address) + 3, "[%s]", + config.server_address); free(config.server_address); config.server_address = new_server_address; } @@ -494,8 +520,9 @@ mp_state_enum check_http(check_curl_config config) { /* compose URL: use the address we want to connect to, set Host: header later */ char url[DEFAULT_BUFFER_SIZE]; snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", config.use_ssl ? "https" : "http", - (config.use_ssl & (config.host_name != NULL)) ? config.host_name : config.server_address, config.server_port, - config.server_url); + (config.use_ssl & (config.host_name != NULL)) ? config.host_name + : config.server_address, + config.server_port, config.server_url); if (verbose >= 1) { printf("* curl CURLOPT_URL: %s\n", url); @@ -503,14 +530,19 @@ mp_state_enum check_http(check_curl_config config) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, url), "CURLOPT_URL"); /* extract proxy information for legacy proxy https requests */ - if (!strcmp(config.http_method, "CONNECT") || strstr(config.server_url, "http") == config.server_url) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXY, config.server_address), "CURLOPT_PROXY"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYPORT, (long)config.server_port), "CURLOPT_PROXYPORT"); + if (!strcmp(config.http_method, "CONNECT") || + strstr(config.server_url, "http") == config.server_url) { + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXY, config.server_address), + "CURLOPT_PROXY"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_PROXYPORT, (long)config.server_port), + "CURLOPT_PROXYPORT"); if (verbose >= 2) { printf("* curl CURLOPT_PROXY: %s:%d\n", config.server_address, config.server_port); } config.http_method = "GET"; - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, config.server_url), "CURLOPT_URL"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, config.server_url), + "CURLOPT_URL"); } /* disable body for HEAD request */ @@ -519,16 +551,21 @@ mp_state_enum check_http(check_curl_config config) { } /* set HTTP protocol version */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, config.curl_http_version), "CURLOPT_HTTP_VERSION"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, config.curl_http_version), + "CURLOPT_HTTP_VERSION"); /* set HTTP method */ if (config.http_method) { if (!strcmp(config.http_method, "POST")) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POST, 1), "CURLOPT_POST"); } else if (!strcmp(config.http_method, "PUT")) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_UPLOAD, 1), + "CURLOPT_UPLOAD"); } else { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, config.http_method), "CURLOPT_CUSTOMREQUEST"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, config.http_method), + "CURLOPT_CUSTOMREQUEST"); } } @@ -545,8 +582,10 @@ mp_state_enum check_http(check_curl_config config) { /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in anyway */ char http_header[DEFAULT_BUFFER_SIZE]; if (config.host_name != NULL && force_host_header == NULL) { - if ((config.virtual_port != HTTP_PORT && !config.use_ssl) || (config.virtual_port != HTTPS_PORT && config.use_ssl)) { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", config.host_name, config.virtual_port); + if ((config.virtual_port != HTTP_PORT && !config.use_ssl) || + (config.virtual_port != HTTPS_PORT && config.use_ssl)) { + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", config.host_name, + config.virtual_port); } else { snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", config.host_name); } @@ -563,43 +602,53 @@ mp_state_enum check_http(check_curl_config config) { for (int i = 0; i < config.http_opt_headers_count; i++) { header_list = curl_slist_append(header_list, config.http_opt_headers[i]); } - /* This cannot be free'd here because a redirection will then try to access this and segfault */ + /* This cannot be free'd here because a redirection will then try to access this and + * segfault */ /* Covered in a testcase in tests/check_http.t */ /* free(http_opt_headers); */ } /* set HTTP headers */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list), "CURLOPT_HTTPHEADER"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list), + "CURLOPT_HTTPHEADER"); #ifdef LIBCURL_FEATURE_SSL /* set SSL version, warn about insecure or unsupported versions */ if (config.use_ssl) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLVERSION, config.ssl_version), "CURLOPT_SSLVERSION"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_SSLVERSION, config.ssl_version), "CURLOPT_SSLVERSION"); } /* client certificate and key to present to server (SSL) */ if (config.client_cert) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLCERT, config.client_cert), "CURLOPT_SSLCERT"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLCERT, config.client_cert), + "CURLOPT_SSLCERT"); } if (config.client_privkey) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLKEY, config.client_privkey), "CURLOPT_SSLKEY"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_SSLKEY, config.client_privkey), "CURLOPT_SSLKEY"); } if (config.ca_cert) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CAINFO, config.ca_cert), "CURLOPT_CAINFO"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CAINFO, config.ca_cert), + "CURLOPT_CAINFO"); } if (config.ca_cert || config.verify_peer_and_host) { /* per default if we have a CA verify both the peer and the * hostname in the certificate, can be switched off later */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1), "CURLOPT_SSL_VERIFYPEER"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2), "CURLOPT_SSL_VERIFYHOST"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1), + "CURLOPT_SSL_VERIFYPEER"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2), + "CURLOPT_SSL_VERIFYHOST"); } else { /* backward-compatible behaviour, be tolerant in checks * TODO: depending on more options have aspects we want * to be less tolerant about ssl verfications */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0), "CURLOPT_SSL_VERIFYPEER"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0), "CURLOPT_SSL_VERIFYHOST"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0), + "CURLOPT_SSL_VERIFYPEER"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0), + "CURLOPT_SSL_VERIFYHOST"); } /* detect SSL library used by libcurl */ @@ -621,15 +670,19 @@ mp_state_enum check_http(check_curl_config config) { # endif /* USE_OPENSSL */ /* libcurl is built with OpenSSL, monitoring plugins, so falling * back to manually extracting certificate information */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), + "CURLOPT_CERTINFO"); break; case CURLHELP_SSL_LIBRARY_NSS: # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) /* NSS: support for CERTINFO is implemented since 7.34.0 */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), + "CURLOPT_CERTINFO"); # else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ - die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library '%s' is too old)\n", + die(STATE_CRITICAL, + "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library " + "'%s' is too old)\n", curlhelp_get_ssl_library_string(ssl_library)); # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ break; @@ -637,50 +690,63 @@ mp_state_enum check_http(check_curl_config config) { case CURLHELP_SSL_LIBRARY_GNUTLS: # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), + "CURLOPT_CERTINFO"); # else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */ - die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library '%s' is too old)\n", + die(STATE_CRITICAL, + "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library " + "'%s' is too old)\n", curlhelp_get_ssl_library_string(ssl_library)); # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */ break; case CURLHELP_SSL_LIBRARY_UNKNOWN: default: - die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (unknown SSL library '%s', must implement first)\n", + die(STATE_CRITICAL, + "HTTP CRITICAL - Cannot retrieve certificates (unknown SSL library '%s', must " + "implement first)\n", curlhelp_get_ssl_library_string(ssl_library)); break; } # else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ /* old libcurl, our only hope is OpenSSL, otherwise we are out of luck */ - if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL || ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL) { + if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL || + ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL) { add_sslctx_verify_fun = true; } else { - die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl " + die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no " + "CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl " "too old and has no CURLOPT_CERTINFO)\n"); } # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ } -# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 10, 6) /* required for CURLOPT_SSL_CTX_FUNCTION */ +# if LIBCURL_VERSION_NUM >= \ + MAKE_LIBCURL_VERSION(7, 10, 6) /* required for CURLOPT_SSL_CTX_FUNCTION */ // ssl ctx function is not available with all ssl backends if (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, NULL) != CURLE_UNKNOWN_OPTION) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), + "CURLOPT_SSL_CTX_FUNCTION"); } # endif #endif /* LIBCURL_FEATURE_SSL */ /* set default or user-given user agent identification */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERAGENT, config.user_agent), "CURLOPT_USERAGENT"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERAGENT, config.user_agent), + "CURLOPT_USERAGENT"); /* proxy-authentication */ if (strcmp(config.proxy_auth, "")) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, config.proxy_auth), "CURLOPT_PROXYUSERPWD"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, config.proxy_auth), + "CURLOPT_PROXYUSERPWD"); } /* authentication */ if (strcmp(config.user_auth, "")) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERPWD, config.user_auth), "CURLOPT_USERPWD"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERPWD, config.user_auth), + "CURLOPT_USERPWD"); } /* TODO: parameter auth method, bitfield of following methods: @@ -695,26 +761,32 @@ mp_state_enum check_http(check_curl_config config) { * CURLAUTH_ANYSAFE: most secure, without BASIC * or CURLAUTH_ANY: most secure, even BASIC if necessary * - * handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_DIGEST ), "CURLOPT_HTTPAUTH"); + * handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPAUTH, + * (long)CURLAUTH_DIGEST ), "CURLOPT_HTTPAUTH"); */ /* handle redirections */ if (config.onredirect == STATE_DEPENDENT) { if (config.followmethod == FOLLOW_LIBCURL) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1), "CURLOPT_FOLLOWLOCATION"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1), + "CURLOPT_FOLLOWLOCATION"); /* default -1 is infinite, not good, could lead to zombie plugins! Setting it to one bigger than maximal limit to handle errors nicely below */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_MAXREDIRS, config.max_depth + 1), "CURLOPT_MAXREDIRS"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_MAXREDIRS, config.max_depth + 1), + "CURLOPT_MAXREDIRS"); /* for now allow only http and https (we are a http(s) check plugin in the end) */ #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 85, 0) - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"), - "CURLOPT_REDIR_PROTOCOLS_STR"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"), + "CURLOPT_REDIR_PROTOCOLS_STR"); #elif LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4) - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS), - "CURLOPT_REDIRECT_PROTOCOLS"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS), + "CURLOPT_REDIRECT_PROTOCOLS"); #endif /* TODO: handle the following aspects of redirection, make them @@ -722,7 +794,8 @@ mp_state_enum check_http(check_curl_config config) { CURLOPT_POSTREDIR: method switch CURLINFO_REDIRECT_URL: custom redirect option CURLOPT_REDIRECT_PROTOCOLS: allow people to step outside safe protocols - CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range option here is nice like for expected page size? + CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range option + here is nice like for expected page size? */ } else { /* old style redirection is handled below */ @@ -736,8 +809,9 @@ mp_state_enum check_http(check_curl_config config) { /* IPv4 or IPv6 forced DNS resolution */ if (address_family == AF_UNSPEC) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), - "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), + "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)"); } else if (address_family == AF_INET) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)"); @@ -753,7 +827,8 @@ mp_state_enum check_http(check_curl_config config) { if (!strcmp(config.http_method, "POST") || !strcmp(config.http_method, "PUT")) { /* set content of payload for POST and PUT */ if (config.http_content_type) { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", config.http_content_type); + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", + config.http_content_type); header_list = curl_slist_append(header_list, http_header); } /* NULL indicates "HTTP Continue" in libcurl, provide an empty string @@ -763,27 +838,42 @@ mp_state_enum check_http(check_curl_config config) { } if (!strcmp(config.http_method, "POST")) { /* POST method, set payload with CURLOPT_POSTFIELDS */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POSTFIELDS, config.http_post_data), "CURLOPT_POSTFIELDS"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, config.http_post_data), + "CURLOPT_POSTFIELDS"); } else if (!strcmp(config.http_method, "PUT")) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_READFUNCTION, (curl_read_callback)curlhelp_buffer_read_callback), - "CURLOPT_READFUNCTION"); - if (curlhelp_initreadbuffer(&put_buf, config.http_post_data, strlen(config.http_post_data)) < 0) { - die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating read buffer for PUT\n"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_READFUNCTION, + (curl_read_callback)curlhelp_buffer_read_callback), + "CURLOPT_READFUNCTION"); + if (curlhelp_initreadbuffer(&put_buf, config.http_post_data, + strlen(config.http_post_data)) < 0) { + die(STATE_UNKNOWN, + "HTTP CRITICAL - out of memory allocating read buffer for PUT\n"); } put_buf_initialized = true; - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_READDATA, (void *)&put_buf), "CURLOPT_READDATA"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_INFILESIZE, (curl_off_t)strlen(config.http_post_data)), - "CURLOPT_INFILESIZE"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_READDATA, (void *)&put_buf), "CURLOPT_READDATA"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_INFILESIZE, + (curl_off_t)strlen(config.http_post_data)), + "CURLOPT_INFILESIZE"); } } /* cookie handling */ if (config.cookie_jar_file != NULL) { - /* enable reading cookies from a file, and if the filename is an empty string, only enable the curl cookie engine */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEFILE, config.cookie_jar_file), "CURLOPT_COOKIEFILE"); - /* now enable saving cookies to a file, but only if the filename is not an empty string, since writing it would fail */ + /* enable reading cookies from a file, and if the filename is an empty string, only enable + * the curl cookie engine */ + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_COOKIEFILE, config.cookie_jar_file), + "CURLOPT_COOKIEFILE"); + /* now enable saving cookies to a file, but only if the filename is not an empty string, + * since writing it would fail */ if (*config.cookie_jar_file) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEJAR, config.cookie_jar_file), "CURLOPT_COOKIEJAR"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_COOKIEJAR, config.cookie_jar_file), + "CURLOPT_COOKIEJAR"); } } @@ -806,7 +896,8 @@ mp_state_enum check_http(check_curl_config config) { /* Curl errors, result in critical Nagios state */ if (res != CURLE_OK) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"), + snprintf(msg, DEFAULT_BUFFER_SIZE, + _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"), config.server_port, res, errbuf[0] ? errbuf : curl_easy_strerror(res)); die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } @@ -821,13 +912,14 @@ mp_state_enum check_http(check_curl_config config) { /* check certificate with OpenSSL functions, curl has been built against OpenSSL * and we actually have OpenSSL in the monitoring tools */ - result_ssl = np_net_ssl_check_certificate(cert, config.days_till_exp_warn, config.days_till_exp_crit); + result_ssl = np_net_ssl_check_certificate(cert, config.days_till_exp_warn, + config.days_till_exp_crit); if (!config.continue_after_check_cert) { return result_ssl; } # else /* USE_OPENSSL */ - die(STATE_CRITICAL, - "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL callback used and not linked against OpenSSL\n"); + die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL " + "callback used and not linked against OpenSSL\n"); # endif /* USE_OPENSSL */ } else { struct curl_slist *slist; @@ -841,7 +933,8 @@ mp_state_enum check_http(check_curl_config config) { */ const char *raw_cert = NULL; for (int i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) { - for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) { + for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; + slist = slist->next) { if (verbose >= 2) { printf("%d ** %s\n", i, slist->data); } @@ -854,32 +947,38 @@ mp_state_enum check_http(check_curl_config config) { GOT_FIRST_CERT: if (!raw_cert) { snprintf(msg, DEFAULT_BUFFER_SIZE, - _("Cannot retrieve certificates from CERTINFO information - certificate data was empty")); + _("Cannot retrieve certificates from CERTINFO information - " + "certificate data was empty")); die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } BIO *cert_BIO = BIO_new(BIO_s_mem()); BIO_write(cert_BIO, raw_cert, strlen(raw_cert)); cert = PEM_read_bio_X509(cert_BIO, NULL, NULL, NULL); if (!cert) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Cannot read certificate from CERTINFO information - BIO error")); + snprintf( + msg, DEFAULT_BUFFER_SIZE, + _("Cannot read certificate from CERTINFO information - BIO error")); die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } BIO_free(cert_BIO); - result_ssl = np_net_ssl_check_certificate(cert, config.days_till_exp_warn, config.days_till_exp_crit); + result_ssl = np_net_ssl_check_certificate(cert, config.days_till_exp_warn, + config.days_till_exp_crit); if (!config.continue_after_check_cert) { return result_ssl; } # else /* USE_OPENSSL */ - /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our disposal, - * so we use the libcurl CURLINFO data + /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our + * disposal, so we use the libcurl CURLINFO data */ - result_ssl = net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn, days_till_exp_crit); + result_ssl = net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn, + days_till_exp_crit); if (!continue_after_check_cert) { return result_ssl; } # endif /* USE_OPENSSL */ } else { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Cannot retrieve certificates - cURL returned %d - %s"), res, + snprintf(msg, DEFAULT_BUFFER_SIZE, + _("Cannot retrieve certificates - cURL returned %d - %s"), res, curl_easy_strerror(res)); die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } @@ -892,28 +991,40 @@ mp_state_enum check_http(check_curl_config config) { * performance data to the answer always */ double total_time; - handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total_time), "CURLINFO_TOTAL_TIME"); + handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total_time), + "CURLINFO_TOTAL_TIME"); int page_len = get_content_length(&header_buf, &body_buf); char perfstring[DEFAULT_BUFFER_SIZE]; if (config.show_extended_perfdata) { double time_connect; - handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &time_connect), "CURLINFO_CONNECT_TIME"); + handle_curl_option_return_code( + curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &time_connect), "CURLINFO_CONNECT_TIME"); double time_appconnect; - handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), "CURLINFO_APPCONNECT_TIME"); + handle_curl_option_return_code( + curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), + "CURLINFO_APPCONNECT_TIME"); double time_headers; - handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, &time_headers), "CURLINFO_PRETRANSFER_TIME"); + handle_curl_option_return_code( + curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, &time_headers), + "CURLINFO_PRETRANSFER_TIME"); double time_firstbyte; - handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), - "CURLINFO_STARTTRANSFER_TIME"); - - snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s", perfd_time(total_time, config.thlds, config.socket_timeout), - perfd_size(page_len, config.min_page_len), perfd_time_connect(time_connect, config.socket_timeout), - config.use_ssl ? perfd_time_ssl(time_appconnect - time_connect, config.socket_timeout) : "", + handle_curl_option_return_code( + curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), + "CURLINFO_STARTTRANSFER_TIME"); + + snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s", + perfd_time(total_time, config.thlds, config.socket_timeout), + perfd_size(page_len, config.min_page_len), + perfd_time_connect(time_connect, config.socket_timeout), + config.use_ssl + ? perfd_time_ssl(time_appconnect - time_connect, config.socket_timeout) + : "", perfd_time_headers(time_headers - time_appconnect, config.socket_timeout), perfd_time_firstbyte(time_firstbyte - time_headers, config.socket_timeout), perfd_time_transfer(total_time - time_firstbyte, config.socket_timeout)); } else { - snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s", perfd_time(total_time, config.thlds, config.socket_timeout), + snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s", + perfd_time(total_time, config.thlds, config.socket_timeout), perfd_size(page_len, config.min_page_len)); } @@ -924,37 +1035,45 @@ mp_state_enum check_http(check_curl_config config) { /* get status line of answer, check sanity of HTTP code */ if (curlhelp_parse_statusline(header_buf.buf, &status_line) < 0) { - snprintf(msg, DEFAULT_BUFFER_SIZE, "Unparsable status line in %.3g seconds response time|%s\n", total_time, perfstring); + snprintf(msg, DEFAULT_BUFFER_SIZE, + "Unparsable status line in %.3g seconds response time|%s\n", total_time, + perfstring); /* we cannot know the major/minor version here for sure as we cannot parse the first line */ die(STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x %ld unknown - %s", code, msg); } status_line_initialized = true; /* get result code from cURL */ - handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code), "CURLINFO_RESPONSE_CODE"); + handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code), + "CURLINFO_RESPONSE_CODE"); if (verbose >= 2) { printf("* curl CURLINFO_RESPONSE_CODE is %ld\n", code); } /* print status line, header, body if verbose */ if (verbose >= 2) { - printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header_buf.buf, (config.no_body ? " [[ skipped ]]" : body_buf.buf)); + printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header_buf.buf, + (config.no_body ? " [[ skipped ]]" : body_buf.buf)); } /* make sure the status line matches the response we are looking for */ if (!expected_statuscode(status_line.first_line, config.server_expect)) { if (config.server_port == HTTP_PORT) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), status_line.first_line); - } else { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), config.server_port, + snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), status_line.first_line); + } else { + snprintf(msg, DEFAULT_BUFFER_SIZE, + _("Invalid HTTP response received from host on port %d: %s\n"), + config.server_port, status_line.first_line); } - die(STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg, config.show_body ? "\n" : "", config.show_body ? body_buf.buf : ""); + die(STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg, config.show_body ? "\n" : "", + config.show_body ? body_buf.buf : ""); } int result = STATE_OK; if (config.server_expect_yn) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), config.server_expect); + snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), + config.server_expect); if (verbose) { printf("%s\n", msg); } @@ -962,7 +1081,8 @@ mp_state_enum check_http(check_curl_config config) { } else { /* illegal return codes result in a critical state */ if (code >= 600 || code < 100) { - die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), status_line.http_code, status_line.msg); + die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), + status_line.http_code, status_line.msg); /* server errors result in a critical state */ } else if (code >= 500) { result = STATE_CRITICAL; @@ -995,12 +1115,15 @@ mp_state_enum check_http(check_curl_config config) { /* libcurl redirection internally, handle error states here */ if (config.followmethod == FOLLOW_LIBCURL) { - handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &redir_depth), "CURLINFO_REDIRECT_COUNT"); + handle_curl_option_return_code( + curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &redir_depth), + "CURLINFO_REDIRECT_COUNT"); if (verbose >= 2) { printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth); } if (redir_depth > config.max_depth) { - snprintf(msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl", config.max_depth); + snprintf(msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl", + config.max_depth); die(STATE_WARNING, "HTTP WARNING - %s", msg); } } @@ -1008,7 +1131,8 @@ mp_state_enum check_http(check_curl_config config) { /* check status codes, set exit status accordingly */ if (status_line.http_code != code) { die(STATE_CRITICAL, _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"), - string_statuscode(status_line.http_major, status_line.http_minor), status_line.http_code, status_line.msg, code); + string_statuscode(status_line.http_major, status_line.http_minor), + status_line.http_code, status_line.msg, code); } if (config.maximum_age >= 0) { @@ -1029,9 +1153,10 @@ mp_state_enum check_http(check_curl_config config) { char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sheader '%s' not found on '%s://%s:%d%s', "), msg, output_header_search, - config.use_ssl ? "https" : "http", config.host_name ? config.host_name : config.server_address, config.server_port, - config.server_url); + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sheader '%s' not found on '%s://%s:%d%s', "), + msg, output_header_search, config.use_ssl ? "https" : "http", + config.host_name ? config.host_name : config.server_address, + config.server_port, config.server_url); strcpy(msg, tmp); @@ -1051,9 +1176,10 @@ mp_state_enum check_http(check_curl_config config) { char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sstring '%s' not found on '%s://%s:%d%s', "), msg, output_string_search, - config.use_ssl ? "https" : "http", config.host_name ? config.host_name : config.server_address, config.server_port, - config.server_url); + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sstring '%s' not found on '%s://%s:%d%s', "), + msg, output_string_search, config.use_ssl ? "https" : "http", + config.host_name ? config.host_name : config.server_address, + config.server_port, config.server_url); strcpy(msg, tmp); @@ -1065,10 +1191,12 @@ mp_state_enum check_http(check_curl_config config) { regex_t preg; regmatch_t pmatch[REGS]; int errcode = regexec(&preg, body_buf.buf, REGS, pmatch, 0); - if ((errcode == 0 && !config.invert_regex) || (errcode == REG_NOMATCH && config.invert_regex)) { + if ((errcode == 0 && !config.invert_regex) || + (errcode == REG_NOMATCH && config.invert_regex)) { /* OK - No-op to avoid changing the logic around it */ result = max_state_alt(STATE_OK, result); - } else if ((errcode == REG_NOMATCH && !config.invert_regex) || (errcode == 0 && config.invert_regex)) { + } else if ((errcode == REG_NOMATCH && !config.invert_regex) || + (errcode == 0 && config.invert_regex)) { if (!config.invert_regex) { char tmp[DEFAULT_BUFFER_SIZE]; @@ -1123,10 +1251,13 @@ mp_state_enum check_http(check_curl_config config) { } } - /* TODO: separate _() msg and status code: die (result, "HTTP %s: %s\n", state_text(result), msg); */ - die(max_state_alt(result, result_ssl), "HTTP %s: %s %d %s%s%s - %d bytes in %.3f second response time %s|%s\n%s%s", state_text(result), - string_statuscode(status_line.http_major, status_line.http_minor), status_line.http_code, status_line.msg, - strlen(msg) > 0 ? " - " : "", msg, page_len, total_time, (config.display_html ? "" : ""), perfstring, + /* TODO: separate _() msg and status code: die (result, "HTTP %s: %s\n", state_text(result), + * msg); */ + die(max_state_alt(result, result_ssl), + "HTTP %s: %s %d %s%s%s - %d bytes in %.3f second response time %s|%s\n%s%s", + state_text(result), string_statuscode(status_line.http_major, status_line.http_minor), + status_line.http_code, status_line.msg, strlen(msg) > 0 ? " - " : "", msg, page_len, + total_time, (config.display_html ? "" : ""), perfstring, (config.show_body ? body_buf.buf : ""), (config.show_body ? "\n" : "")); return max_state_alt(result, result_ssl); @@ -1157,8 +1288,9 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config) { struct phr_header headers[255]; size_t msglen; size_t nof_headers = 255; - int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor, - &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0); + int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, + &status_line.http_minor, &status_line.http_code, &status_line.msg, + &msglen, headers, &nof_headers, 0); if (res == -1) { die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); @@ -1171,8 +1303,8 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config) { } if (++redir_depth > config.max_depth) { - die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s%s\n"), config.max_depth, location, - (config.display_html ? "" : "")); + die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s%s\n"), + config.max_depth, location, (config.display_html ? "" : "")); } UriParserStateA state; @@ -1180,8 +1312,8 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config) { state.uri = &uri; if (uriParseUriA(&state, location) != URI_SUCCESS) { if (state.errorCode == URI_ERROR_SYNTAX) { - die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location '%s'%s\n"), location, - (config.display_html ? "" : "")); + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location '%s'%s\n"), + location, (config.display_html ? "" : "")); } else if (state.errorCode == URI_ERROR_MALLOC) { die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); } @@ -1234,8 +1366,8 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config) { } } if (new_port > MAX_PORT) { - die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s%s\n"), MAX_PORT, location, - config.display_html ? "" : ""); + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s%s\n"), MAX_PORT, + location, config.display_html ? "" : ""); } /* by RFC 7231 relative URLs in Location should be taken relative to @@ -1259,14 +1391,19 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config) { const UriPathSegmentA *p = uri.pathHead; for (; p; p = p->next) { strncat(new_url, "/", DEFAULT_BUFFER_SIZE); - strncat(new_url, uri_string(p->text, buf, DEFAULT_BUFFER_SIZE), DEFAULT_BUFFER_SIZE - 1); + strncat(new_url, uri_string(p->text, buf, DEFAULT_BUFFER_SIZE), + DEFAULT_BUFFER_SIZE - 1); } } - if (config.server_port == new_port && !strncmp(config.server_address, new_host, MAX_IPV4_HOSTLENGTH) && - (config.host_name && !strncmp(config.host_name, new_host, MAX_IPV4_HOSTLENGTH)) && !strcmp(config.server_url, new_url)) { - die(STATE_CRITICAL, _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), config.use_ssl ? "https" : "http", - new_host, new_port, new_url, (config.display_html ? "" : "")); + if (config.server_port == new_port && + !strncmp(config.server_address, new_host, MAX_IPV4_HOSTLENGTH) && + (config.host_name && !strncmp(config.host_name, new_host, MAX_IPV4_HOSTLENGTH)) && + !strcmp(config.server_url, new_url)) { + die(STATE_CRITICAL, + _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), + config.use_ssl ? "https" : "http", new_host, new_port, new_url, + (config.display_html ? "" : "")); } /* set new values for redirected request */ @@ -1293,7 +1430,8 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config) { if (verbose) { printf(_("Redirection to %s://%s:%d%s\n"), config.use_ssl ? "https" : "http", - config.host_name ? config.host_name : config.server_address, config.server_port, config.server_url); + config.host_name ? config.host_name : config.server_address, config.server_port, + config.server_url); } /* TODO: the hash component MUST be taken from the original URL and @@ -1326,50 +1464,51 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { STATE_REGEX }; - static struct option longopts[] = {STD_LONG_OPTS, - {"link", no_argument, 0, 'L'}, - {"nohtml", no_argument, 0, 'n'}, - {"ssl", optional_argument, 0, 'S'}, - {"sni", no_argument, 0, SNI_OPTION}, - {"post", required_argument, 0, 'P'}, - {"method", required_argument, 0, 'j'}, - {"IP-address", required_argument, 0, 'I'}, - {"url", required_argument, 0, 'u'}, - {"port", required_argument, 0, 'p'}, - {"authorization", required_argument, 0, 'a'}, - {"proxy-authorization", required_argument, 0, 'b'}, - {"header-string", required_argument, 0, 'd'}, - {"string", required_argument, 0, 's'}, - {"expect", required_argument, 0, 'e'}, - {"regex", required_argument, 0, 'r'}, - {"ereg", required_argument, 0, 'r'}, - {"eregi", required_argument, 0, 'R'}, - {"linespan", no_argument, 0, 'l'}, - {"onredirect", required_argument, 0, 'f'}, - {"certificate", required_argument, 0, 'C'}, - {"client-cert", required_argument, 0, 'J'}, - {"private-key", required_argument, 0, 'K'}, - {"ca-cert", required_argument, 0, CA_CERT_OPTION}, - {"verify-cert", no_argument, 0, 'D'}, - {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT}, - {"useragent", required_argument, 0, 'A'}, - {"header", required_argument, 0, 'k'}, - {"no-body", no_argument, 0, 'N'}, - {"max-age", required_argument, 0, 'M'}, - {"content-type", required_argument, 0, 'T'}, - {"pagesize", required_argument, 0, 'm'}, - {"invert-regex", no_argument, NULL, INVERT_REGEX}, - {"state-regex", required_argument, 0, STATE_REGEX}, - {"use-ipv4", no_argument, 0, '4'}, - {"use-ipv6", no_argument, 0, '6'}, - {"extended-perfdata", no_argument, 0, 'E'}, - {"show-body", no_argument, 0, 'B'}, - {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION}, - {"http-version", required_argument, 0, HTTP_VERSION_OPTION}, - {"enable-automatic-decompression", no_argument, 0, AUTOMATIC_DECOMPRESSION}, - {"cookie-jar", required_argument, 0, COOKIE_JAR}, - {"haproxy-protocol", no_argument, 0, HAPROXY_PROTOCOL}, - {0, 0, 0, 0}}; + static struct option longopts[] = { + STD_LONG_OPTS, + {"link", no_argument, 0, 'L'}, + {"nohtml", no_argument, 0, 'n'}, + {"ssl", optional_argument, 0, 'S'}, + {"sni", no_argument, 0, SNI_OPTION}, + {"post", required_argument, 0, 'P'}, + {"method", required_argument, 0, 'j'}, + {"IP-address", required_argument, 0, 'I'}, + {"url", required_argument, 0, 'u'}, + {"port", required_argument, 0, 'p'}, + {"authorization", required_argument, 0, 'a'}, + {"proxy-authorization", required_argument, 0, 'b'}, + {"header-string", required_argument, 0, 'd'}, + {"string", required_argument, 0, 's'}, + {"expect", required_argument, 0, 'e'}, + {"regex", required_argument, 0, 'r'}, + {"ereg", required_argument, 0, 'r'}, + {"eregi", required_argument, 0, 'R'}, + {"linespan", no_argument, 0, 'l'}, + {"onredirect", required_argument, 0, 'f'}, + {"certificate", required_argument, 0, 'C'}, + {"client-cert", required_argument, 0, 'J'}, + {"private-key", required_argument, 0, 'K'}, + {"ca-cert", required_argument, 0, CA_CERT_OPTION}, + {"verify-cert", no_argument, 0, 'D'}, + {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT}, + {"useragent", required_argument, 0, 'A'}, + {"header", required_argument, 0, 'k'}, + {"no-body", no_argument, 0, 'N'}, + {"max-age", required_argument, 0, 'M'}, + {"content-type", required_argument, 0, 'T'}, + {"pagesize", required_argument, 0, 'm'}, + {"invert-regex", no_argument, NULL, INVERT_REGEX}, + {"state-regex", required_argument, 0, STATE_REGEX}, + {"use-ipv4", no_argument, 0, '4'}, + {"use-ipv6", no_argument, 0, '6'}, + {"extended-perfdata", no_argument, 0, 'E'}, + {"show-body", no_argument, 0, 'B'}, + {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION}, + {"http-version", required_argument, 0, HTTP_VERSION_OPTION}, + {"enable-automatic-decompression", no_argument, 0, AUTOMATIC_DECOMPRESSION}, + {"cookie-jar", required_argument, 0, COOKIE_JAR}, + {"haproxy-protocol", no_argument, 0, HAPROXY_PROTOCOL}, + {0, 0, 0, 0}}; check_curl_config_wrapper result = { .errorcode = OK, @@ -1407,7 +1546,9 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { bool specify_port = false; while (true) { - int option_index = getopt_long(argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:DnlLS::m:M:NEB", longopts, &option); + int option_index = getopt_long( + argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:DnlLS::m:M:NEB", + longopts, &option); if (option_index == -1 || option_index == EOF || option_index == 1) { break; } @@ -1450,7 +1591,8 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { free(result.config.host_name); result.config.host_name = strndup(optarg, host_name_length); } - } else if ((p = strchr(result.config.host_name, ':')) != NULL && strchr(++p, ':') == NULL) { /* IPv4:port or host:port */ + } else if ((p = strchr(result.config.host_name, ':')) != NULL && + strchr(++p, ':') == NULL) { /* IPv4:port or host:port */ result.config.virtual_port = atoi(p); /* cut off the port */ host_name_length = strlen(result.config.host_name) - strlen(p) - 1; @@ -1503,10 +1645,12 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { break; case 'k': /* Additional headers */ if (result.config.http_opt_headers_count == 0) { - result.config.http_opt_headers = malloc(sizeof(char *) * (++result.config.http_opt_headers_count)); + result.config.http_opt_headers = + malloc(sizeof(char *) * (++result.config.http_opt_headers_count)); } else { result.config.http_opt_headers = - realloc(result.config.http_opt_headers, sizeof(char *) * (++result.config.http_opt_headers_count)); + realloc(result.config.http_opt_headers, + sizeof(char *) * (++result.config.http_opt_headers_count)); } result.config.http_opt_headers[result.config.http_opt_headers_count - 1] = optarg; break; @@ -1615,7 +1759,8 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { result.config.ssl_version = CURL_SSLVERSION_DEFAULT; # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */ else { - usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 (with optional '+' suffix)")); + usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 " + "(with optional '+' suffix)")); } } # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) @@ -1681,21 +1826,26 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } else if (!strcmp(optarg, "follow")) { result.config.onredirect = STATE_DEPENDENT; } else if (!strcmp(optarg, "stickyport")) { - result.config.onredirect = STATE_DEPENDENT, result.config.followmethod = FOLLOW_HTTP_CURL, + result.config.onredirect = STATE_DEPENDENT, + result.config.followmethod = FOLLOW_HTTP_CURL, result.config.followsticky = STICKY_HOST | STICKY_PORT; } else if (!strcmp(optarg, "sticky")) { - result.config.onredirect = STATE_DEPENDENT, result.config.followmethod = FOLLOW_HTTP_CURL, + result.config.onredirect = STATE_DEPENDENT, + result.config.followmethod = FOLLOW_HTTP_CURL, result.config.followsticky = STICKY_HOST; } else if (!strcmp(optarg, "follow")) { - result.config.onredirect = STATE_DEPENDENT, result.config.followmethod = FOLLOW_HTTP_CURL, + result.config.onredirect = STATE_DEPENDENT, + result.config.followmethod = FOLLOW_HTTP_CURL, result.config.followsticky = STICKY_NONE; } else if (!strcmp(optarg, "curl")) { - result.config.onredirect = STATE_DEPENDENT, result.config.followmethod = FOLLOW_LIBCURL; + result.config.onredirect = STATE_DEPENDENT, + result.config.followmethod = FOLLOW_LIBCURL; } else { usage2(_("Invalid onredirect option"), optarg); } if (verbose >= 2) { - printf(_("* Following redirects set to %s\n"), state_text(result.config.onredirect)); + printf(_("* Following redirects set to %s\n"), + state_text(result.config.onredirect)); } break; case 'd': /* string or substring */ @@ -1791,7 +1941,8 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { result.config.maximum_age = atoi(optarg) * 60 * 60; } else if (option_length && optarg[option_length - 1] == 'd') { result.config.maximum_age = atoi(optarg) * 60 * 60 * 24; - } else if (option_length && (optarg[option_length - 1] == 's' || isdigit(optarg[option_length - 1]))) { + } else if (option_length && + (optarg[option_length - 1] == 's' || isdigit(optarg[option_length - 1]))) { result.config.maximum_age = atoi(optarg); } else { fprintf(stderr, "unparsable max-age: %s\n", optarg); @@ -1860,7 +2011,8 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { set_thresholds(&result.config.thlds, warning_thresholds, critical_thresholds); - if (critical_thresholds && result.config.thlds->critical->end > (double)result.config.socket_timeout) { + if (critical_thresholds && + result.config.thlds->critical->end > (double)result.config.socket_timeout) { result.config.socket_timeout = (int)result.config.thlds->critical->end + 1; } if (verbose >= 2) { @@ -1890,32 +2042,39 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } char *perfd_time(double elapsed_time, thresholds *thlds, long socket_timeout) { - return fperfdata("time", elapsed_time, "s", thlds->warning, thlds->warning ? thlds->warning->end : 0, thlds->critical, + return fperfdata("time", elapsed_time, "s", thlds->warning, + thlds->warning ? thlds->warning->end : 0, thlds->critical, thlds->critical ? thlds->critical->end : 0, true, 0, true, socket_timeout); } char *perfd_time_connect(double elapsed_time_connect, long socket_timeout) { - return fperfdata("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, socket_timeout); + return fperfdata("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, + socket_timeout); } char *perfd_time_ssl(double elapsed_time_ssl, long socket_timeout) { - return fperfdata("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, socket_timeout); + return fperfdata("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, + socket_timeout); } char *perfd_time_headers(double elapsed_time_headers, long socket_timeout) { - return fperfdata("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, socket_timeout); + return fperfdata("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, + socket_timeout); } char *perfd_time_firstbyte(double elapsed_time_firstbyte, long socket_timeout) { - return fperfdata("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, true, socket_timeout); + return fperfdata("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, + true, socket_timeout); } char *perfd_time_transfer(double elapsed_time_transfer, long socket_timeout) { - return fperfdata("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, true, socket_timeout); + return fperfdata("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, + true, socket_timeout); } char *perfd_size(int page_len, int min_page_len) { - return perfdata("size", page_len, "B", (min_page_len > 0), min_page_len, (min_page_len > 0), 0, true, 0, false, 0); + return perfdata("size", page_len, "B", (min_page_len > 0), min_page_len, (min_page_len > 0), 0, + true, 0, false, 0); } void print_help(void) { @@ -1929,7 +2088,8 @@ void print_help(void) { printf("%s\n", _("strings and regular expressions, check connection times, and report on")); printf("%s\n", _("certificate expiration times.")); printf("\n"); - printf("%s\n", _("It makes use of libcurl to do so. It tries to be as compatible to check_http")); + printf("%s\n", + _("It makes use of libcurl to do so. It tries to be as compatible to check_http")); printf("%s\n", _("as possible.")); printf("\n\n"); @@ -1947,7 +2107,8 @@ void print_help(void) { printf(" %s\n", _("Host name argument for servers using host headers (virtual host)")); printf(" %s\n", _("Append a port to include it in the header (eg: example.com:5000)")); printf(" %s\n", "-I, --IP-address=ADDRESS"); - printf(" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup).")); + printf(" %s\n", + _("IP address or name (use numeric address if possible to bypass DNS lookup).")); printf(" %s\n", "-p, --port=INTEGER"); printf(" %s", _("Port number (default: ")); printf("%d)\n", HTTP_PORT); @@ -1956,27 +2117,37 @@ void print_help(void) { #ifdef LIBCURL_FEATURE_SSL printf(" %s\n", "-S, --ssl=VERSION[+]"); - printf(" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents")); + printf(" %s\n", + _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents")); printf(" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,")); - printf(" %s\n", _("1.2 = TLSv1.2, 1.3 = TLSv1.3). With a '+' suffix, newer versions are also accepted.")); - printf(" %s\n", _("Note: SSLv2, SSLv3, TLSv1.0 and TLSv1.1 are deprecated and are usually disabled in libcurl")); + printf( + " %s\n", + _("1.2 = TLSv1.2, 1.3 = TLSv1.3). With a '+' suffix, newer versions are also accepted.")); + printf(" %s\n", _("Note: SSLv2, SSLv3, TLSv1.0 and TLSv1.1 are deprecated and are usually " + "disabled in libcurl")); printf(" %s\n", "--sni"); printf(" %s\n", _("Enable SSL/TLS hostname extension support (SNI)")); # if LIBCURL_VERSION_NUM >= 0x071801 - printf(" %s\n", _("Note: --sni is the default in libcurl as SSLv2 and SSLV3 are deprecated and")); + printf(" %s\n", + _("Note: --sni is the default in libcurl as SSLv2 and SSLV3 are deprecated and")); printf(" %s\n", _(" SNI only really works since TLSv1.0")); # else printf(" %s\n", _("Note: SNI is not supported in libcurl before 7.18.1")); # endif printf(" %s\n", "-C, --certificate=INTEGER[,INTEGER]"); - printf(" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443.")); - printf(" %s\n", _("A STATE_WARNING is returned if the certificate has a validity less than the")); - printf(" %s\n", _("first agument's value. If there is a second argument and the certificate's")); + printf(" %s\n", + _("Minimum number of days a certificate has to be valid. Port defaults to 443.")); + printf(" %s\n", + _("A STATE_WARNING is returned if the certificate has a validity less than the")); + printf(" %s\n", + _("first agument's value. If there is a second argument and the certificate's")); printf(" %s\n", _("validity is less than its value, a STATE_CRITICAL is returned.")); - printf(" %s\n", _("(When this option is used the URL is not checked by default. You can use")); + printf(" %s\n", + _("(When this option is used the URL is not checked by default. You can use")); printf(" %s\n", _(" --continue-after-certificate to override this behavior)")); printf(" %s\n", "--continue-after-certificate"); - printf(" %s\n", _("Allows the HTTP check to continue after performing the certificate check.")); + printf(" %s\n", + _("Allows the HTTP check to continue after performing the certificate check.")); printf(" %s\n", _("Does nothing unless -C is used.")); printf(" %s\n", "-J, --client-cert=FILE"); printf(" %s\n", _("Name of file that contains the client certificate (PEM format)")); @@ -1994,7 +2165,8 @@ void print_help(void) { printf(" %s\n", _("Comma-delimited list of strings, at least one of them is expected in")); printf(" %s", _("the first (status) line of the server response (default: ")); printf("%s)\n", HTTP_EXPECT); - printf(" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)")); + printf(" %s\n", + _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)")); printf(" %s\n", "-d, --header-string=STRING"); printf(" %s\n", _("String to expect in the response headers")); printf(" %s\n", "-s, --string=STRING"); @@ -2003,7 +2175,8 @@ void print_help(void) { printf(" %s\n", _("URL to GET or POST (default: /)")); printf(" %s\n", "-P, --post=STRING"); printf(" %s\n", _("URL decoded http POST data")); - printf(" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT)"); + printf(" %s\n", + "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT)"); printf(" %s\n", _("Set HTTP method.")); printf(" %s\n", "-N, --no-body"); printf(" %s\n", _("Don't wait for document body: stop reading after headers.")); @@ -2023,7 +2196,8 @@ void print_help(void) { printf(" %s\n", _("Return STATE if found, OK if not (STATE is CRITICAL, per default)")); printf(" %s\n", _("can be changed with --state--regex)")); printf(" %s\n", "--state-regex=STATE"); - printf(" %s\n", _("Return STATE if regex is found, OK if not. STATE can be one of \"critical\",\"warning\"")); + printf(" %s\n", _("Return STATE if regex is found, OK if not. STATE can be one of " + "\"critical\",\"warning\"")); printf(" %s\n", "-a, --authorization=AUTH_PAIR"); printf(" %s\n", _("Username:password on sites with basic authentication")); printf(" %s\n", "-b, --proxy-authorization=AUTH_PAIR"); @@ -2031,7 +2205,9 @@ void print_help(void) { printf(" %s\n", "-A, --useragent=STRING"); printf(" %s\n", _("String to be sent in http header as \"User Agent\"")); printf(" %s\n", "-k, --header=STRING"); - printf(" %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers")); + printf( + " %s\n", + _("Any other tags to be sent in http header. Use multiple times for additional headers")); printf(" %s\n", "-E, --extended-perfdata"); printf(" %s\n", _("Print additional performance data")); printf(" %s\n", "-B, --show-body"); @@ -2047,20 +2223,25 @@ void print_help(void) { printf(" %s", _("Maximal number of redirects (default: ")); printf("%d)\n", DEFAULT_MAX_REDIRS); printf(" %s\n", "-m, --pagesize=INTEGER<:INTEGER>"); - printf(" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)")); + printf(" %s\n", + _("Minimum page size required (bytes) : Maximum page size required (bytes)")); printf("\n"); printf(" %s\n", "--http-version=VERSION"); printf(" %s\n", _("Connect via specific HTTP protocol.")); - printf(" %s\n", _("1.0 = HTTP/1.0, 1.1 = HTTP/1.1, 2.0 = HTTP/2 (HTTP/2 will fail without -S)")); + printf(" %s\n", + _("1.0 = HTTP/1.0, 1.1 = HTTP/1.1, 2.0 = HTTP/2 (HTTP/2 will fail without -S)")); printf(" %s\n", "--enable-automatic-decompression"); printf(" %s\n", _("Enable automatic decompression of body (CURLOPT_ACCEPT_ENCODING).")); printf(" %s\n", "--haproxy-protocol"); printf(" %s\n", _("Send HAProxy proxy protocol v1 header (CURLOPT_HAPROXYPROTOCOL).")); printf(" %s\n", "--cookie-jar=FILE"); printf(" %s\n", _("Store cookies in the cookie jar and send them out when requested.")); - printf(" %s\n", _("Specify an empty string as FILE to enable curl's cookie engine without saving")); - printf(" %s\n", _("the cookies to disk. Only enabling the engine without saving to disk requires")); - printf(" %s\n", _("handling multiple requests internally to curl, so use it with --onredirect=curl")); + printf(" %s\n", + _("Specify an empty string as FILE to enable curl's cookie engine without saving")); + printf(" %s\n", + _("the cookies to disk. Only enabling the engine without saving to disk requires")); + printf(" %s\n", + _("handling multiple requests internally to curl, so use it with --onredirect=curl")); printf("\n"); printf(UT_WARN_CRIT); @@ -2072,10 +2253,13 @@ void print_help(void) { printf("\n"); printf("%s\n", _("Notes:")); printf(" %s\n", _("This plugin will attempt to open an HTTP connection with the host.")); - printf(" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL")); - printf(" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response")); + printf(" %s\n", + _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL")); + printf(" %s\n", + _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response")); printf(" %s\n", _("messages from the host result in STATE_WARNING return values. If you are")); - printf(" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN")); + printf(" %s\n", + _("checking a virtual server that uses 'host headers' you must supply the FQDN")); printf(" %s\n", _("(fully qualified domain name) as the [host_name] argument.")); #ifdef LIBCURL_FEATURE_SSL @@ -2091,38 +2275,53 @@ void print_help(void) { printf("%s\n", _("Examples:")); printf(" %s\n\n", "CHECK CONTENT: check_curl -w 5 -c 10 --ssl -H www.verisign.com"); printf(" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,")); - printf(" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds")); - printf(" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); + printf(" %s\n", + _("a STATE_OK will be returned. When the server returns its content but exceeds")); + printf(" %s\n", + _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); printf(" %s\n", _("a STATE_CRITICAL will be returned.")); printf("\n"); printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 14"); - printf(" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,")); - printf(" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than")); - printf(" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when")); + printf(" %s\n", + _("When the certificate of 'www.verisign.com' is valid for more than 14 days,")); + printf(" %s\n", + _("a STATE_OK is returned. When the certificate is still valid, but for less than")); + printf(" %s\n", + _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when")); printf(" %s\n\n", _("the certificate is expired.")); printf("\n"); printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 30,14"); - printf(" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 30 days,")); - printf(" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than")); + printf(" %s\n", + _("When the certificate of 'www.verisign.com' is valid for more than 30 days,")); + printf(" %s\n", + _("a STATE_OK is returned. When the certificate is still valid, but for less than")); printf(" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned.")); - printf(" %s\n", _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days")); + printf(" %s\n", + _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days")); #endif printf("\n %s\n", "CHECK WEBSERVER CONTENT VIA PROXY:"); printf(" %s\n", _("It is recommended to use an environment proxy like:")); - printf(" %s\n", _("http_proxy=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org")); + printf(" %s\n", + _("http_proxy=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org")); printf(" %s\n", _("legacy proxy requests in check_http style still work:")); - printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u http://www.monitoring-plugins.org/ -H www.monitoring-plugins.org")); + printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u http://www.monitoring-plugins.org/ " + "-H www.monitoring-plugins.org")); #ifdef LIBCURL_FEATURE_SSL printf("\n %s\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: "); printf(" %s\n", _("It is recommended to use an environment proxy like:")); - printf(" %s\n", _("https_proxy=http://192.168.100.35:3128 ./check_curl -H www.verisign.com -S")); + printf(" %s\n", + _("https_proxy=http://192.168.100.35:3128 ./check_curl -H www.verisign.com -S")); printf(" %s\n", _("legacy proxy requests in check_http style still work:")); - printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u https://www.verisign.com/ -S -j CONNECT -H www.verisign.com ")); - printf(" %s\n", _("all these options are needed: -I -p -u -S(sl) -j CONNECT -H ")); - printf(" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds")); - printf(" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); + printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u https://www.verisign.com/ -S -j " + "CONNECT -H www.verisign.com ")); + printf(" %s\n", _("all these options are needed: -I -p -u " + "-S(sl) -j CONNECT -H ")); + printf(" %s\n", + _("a STATE_OK will be returned. When the server returns its content but exceeds")); + printf(" %s\n", + _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); printf(" %s\n", _("a STATE_CRITICAL will be returned.")); #endif @@ -2133,10 +2332,12 @@ void print_help(void) { void print_usage(void) { printf("%s\n", _("Usage:")); printf(" %s -H | -I [-u ] [-p ]\n", progname); - printf(" [-J ] [-K ] [--ca-cert ] [-D]\n"); + printf(" [-J ] [-K ] [--ca-cert ] [-D]\n"); printf(" [-w ] [-c ] [-t ] [-L] [-E] [-a auth]\n"); printf(" [-b proxy_auth] [-f ]\n"); - printf(" [-e ] [-d string] [-s string] [-l] [-r | -R ]\n"); + printf(" [-e ] [-d string] [-s string] [-l] [-r | -R ]\n"); printf(" [-P string] [-m :] [-4|-6] [-N] [-M ]\n"); printf(" [-A string] [-k string] [-S ] [--sni] [--haproxy-protocol]\n"); printf(" [-T ] [-j method]\n"); @@ -2356,22 +2557,26 @@ int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) void curlhelp_free_statusline(curlhelp_statusline *status_line) { free(status_line->first_line); } -char *get_header_value(const struct phr_header *headers, const size_t nof_headers, const char *header) { +char *get_header_value(const struct phr_header *headers, const size_t nof_headers, + const char *header) { for (size_t i = 0; i < nof_headers; i++) { - if (headers[i].name != NULL && strncasecmp(header, headers[i].name, max(headers[i].name_len, 4)) == 0) { + if (headers[i].name != NULL && + strncasecmp(header, headers[i].name, max(headers[i].name_len, 4)) == 0) { return strndup(headers[i].value, headers[i].value_len); } } return NULL; } -int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[DEFAULT_BUFFER_SIZE], int maximum_age) { +int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[DEFAULT_BUFFER_SIZE], + int maximum_age) { struct phr_header headers[255]; size_t nof_headers = 255; curlhelp_statusline status_line; size_t msglen; - int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor, - &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0); + int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, + &status_line.http_minor, &status_line.http_code, &status_line.msg, + &msglen, headers, &nof_headers, 0); if (res == -1) { die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); @@ -2401,26 +2606,30 @@ int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[D time_t srv_data = curl_getdate(server_date, NULL); time_t doc_data = curl_getdate(document_date, NULL); if (verbose >= 2) { - printf("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data, document_date, (int)doc_data); + printf("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data, + document_date, (int)doc_data); } if (srv_data <= 0) { char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date \"%100s\" unparsable, "), *msg, server_date); + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date \"%100s\" unparsable, "), *msg, + server_date); strcpy(*msg, tmp); date_result = max_state_alt(STATE_CRITICAL, date_result); } else if (doc_data <= 0) { char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date); + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument date \"%100s\" unparsable, "), *msg, + document_date); strcpy(*msg, tmp); date_result = max_state_alt(STATE_CRITICAL, date_result); } else if (doc_data > srv_data + 30) { char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data); + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument is %d seconds in the future, "), *msg, + (int)doc_data - (int)srv_data); strcpy(*msg, tmp); date_result = max_state_alt(STATE_CRITICAL, date_result); @@ -2429,14 +2638,16 @@ int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[D if (n > (60 * 60 * 24 * 2)) { char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), *msg, ((float)n) / (60 * 60 * 24)); + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), *msg, + ((float)n) / (60 * 60 * 24)); strcpy(*msg, tmp); date_result = max_state_alt(STATE_CRITICAL, date_result); } else { char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60); + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %d:%02d:%02d ago, "), *msg, + n / (60 * 60), (n / 60) % 60, n % 60); strcpy(*msg, tmp); date_result = max_state_alt(STATE_CRITICAL, date_result); @@ -2454,13 +2665,15 @@ int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[D return date_result; } -int get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_write_curlbuf *body_buf) { +int get_content_length(const curlhelp_write_curlbuf *header_buf, + const curlhelp_write_curlbuf *body_buf) { struct phr_header headers[255]; size_t nof_headers = 255; size_t msglen; curlhelp_statusline status_line; - int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor, - &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0); + int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, + &status_line.http_minor, &status_line.http_code, &status_line.msg, + &msglen, headers, &nof_headers, 0); if (res == -1) { die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); @@ -2514,7 +2727,8 @@ curlhelp_ssl_library curlhelp_get_ssl_library(void) { } if (verbose >= 2) { - printf("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library, ssl_library); + printf("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library, + ssl_library); } free(ssl_version); @@ -2560,7 +2774,8 @@ time_t parse_cert_date(const char *s) { /* TODO: this needs cleanup in the sslutils.c, maybe we the #else case to * OpenSSL could be this function */ -int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_warn, int days_till_exp_crit) { +int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_warn, + int days_till_exp_crit) { struct curl_slist *slist; int cname_found = 0; char *start_date_str = NULL; @@ -2622,14 +2837,16 @@ HAVE_FIRST_CERT: start_date = parse_cert_date(start_date_str); if (start_date <= 0) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Start Date' in certificate: '%s'"), start_date_str); + snprintf(msg, DEFAULT_BUFFER_SIZE, + _("WARNING - Unparsable 'Start Date' in certificate: '%s'"), start_date_str); puts(msg); return STATE_WARNING; } end_date = parse_cert_date(end_date_str); if (end_date <= 0) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Expire Date' in certificate: '%s'"), start_date_str); + snprintf(msg, DEFAULT_BUFFER_SIZE, + _("WARNING - Unparsable 'Expire Date' in certificate: '%s'"), start_date_str); puts(msg); return STATE_WARNING; } @@ -2648,8 +2865,9 @@ HAVE_FIRST_CERT: tzset(); if (days_left > 0 && days_left <= days_till_exp_warn) { - printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", - host_name, days_left, timestamp); + printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), + (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, days_left, + timestamp); if (days_left > days_till_exp_crit) { status = STATE_WARNING; } else { @@ -2662,8 +2880,9 @@ HAVE_FIRST_CERT: time_remaining = (int)time_left / 60; } - printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, - time_remaining, time_left >= 3600 ? "hours" : "minutes", timestamp); + printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"), + (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, time_remaining, + time_left >= 3600 ? "hours" : "minutes", timestamp); if (days_left > days_till_exp_crit) { status = STATE_WARNING; @@ -2674,8 +2893,8 @@ HAVE_FIRST_CERT: printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), host_name, timestamp); status = STATE_CRITICAL; } else if (days_left == 0) { - printf(_("%s - Certificate '%s' just expired (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, - timestamp); + printf(_("%s - Certificate '%s' just expired (%s).\n"), + (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, timestamp); if (days_left > days_till_exp_crit) { status = STATE_WARNING; } else { -- cgit v1.2.3-74-g34f1 From e570ce63634b57573aa84b12d84e7651d466a761 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 14 Jul 2025 23:35:52 +0200 Subject: check_curl: various small improvements --- plugins/check_curl.c | 134 +++++++++++++++++++++--------------------- plugins/check_curl.d/config.h | 12 ++-- 2 files changed, 74 insertions(+), 72 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 5b8a06be..88c5f3e7 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -53,7 +53,7 @@ const char *email = "devel@monitoring-plugins.org"; #include "curl/curl.h" #include "curl/easy.h" -#include "picohttpparser.h" +#include "./picohttpparser/picohttpparser.h" #include "uriparser/Uri.h" @@ -164,7 +164,7 @@ static char *perfd_time_ssl(double elapsed_time_ssl, long /*socket_timeout*/); static char *perfd_time_firstbyte(double elapsed_time_firstbyte, long /*socket_timeout*/); static char *perfd_time_headers(double elapsed_time_headers, long /*socket_timeout*/); static char *perfd_time_transfer(double elapsed_time_transfer, long /*socket_timeout*/); -static char *perfd_size(int page_len, int /*min_page_len*/); +static char *perfd_size(size_t page_len, int /*min_page_len*/); static void print_help(void); void print_usage(void); static void print_curl_version(void); @@ -185,10 +185,10 @@ static int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * static void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/); static char *get_header_value(const struct phr_header *headers, size_t nof_headers, const char *header); -static int check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, - char (*msg)[DEFAULT_BUFFER_SIZE], int /*maximum_age*/); -static int get_content_length(const curlhelp_write_curlbuf *header_buf, - const curlhelp_write_curlbuf *body_buf); +static mp_state_enum check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, + char (*msg)[DEFAULT_BUFFER_SIZE], int /*maximum_age*/); +static size_t get_content_length(const curlhelp_write_curlbuf *header_buf, + const curlhelp_write_curlbuf *body_buf); #if defined(HAVE_SSL) && defined(USE_OPENSSL) int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit); @@ -213,7 +213,7 @@ int main(int argc, char **argv) { const check_curl_config config = tmp_config.config; /* set defaults */ - if (config.user_agent == NULL) { + if (strlen(config.user_agent) == 0) { snprintf(config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", progname, NP_VERSION, VERSION, curl_version()); } @@ -224,7 +224,7 @@ int main(int argc, char **argv) { config.virtual_port ? config.virtual_port : config.server_port, config.server_url); } - exit(check_http(config)); + exit((int)check_http(config)); } #ifdef HAVE_SSL @@ -297,7 +297,7 @@ static char *string_statuscode(int major, int minor) { } /* Checks if the server 'reply' is one of the expected 'statuscodes' */ -static int expected_statuscode(const char *reply, const char *statuscodes) { +static bool expected_statuscode(const char *reply, const char *statuscodes) { char *expected; if ((expected = strdup(statuscodes)) == NULL) { @@ -305,10 +305,10 @@ static int expected_statuscode(const char *reply, const char *statuscodes) { } char *code; - int result = 0; + bool result = false; for (code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) { if (strstr(reply, code) != NULL) { - result = 1; + result = true; break; } } @@ -432,7 +432,7 @@ mp_state_enum check_http(check_curl_config config) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_STDERR, stdout), "CURLOPT_STDERR"); - if (config.automatic_decompression) + if (config.automatic_decompression) { #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""), "CURLOPT_ACCEPT_ENCODING"); @@ -440,6 +440,7 @@ mp_state_enum check_http(check_curl_config config) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING"); #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */ + } /* initialize buffer for body of the answer */ if (curlhelp_initwritebuffer(&body_buf) < 0) { @@ -448,7 +449,7 @@ mp_state_enum check_http(check_curl_config config) { body_buf_initialized = true; handle_curl_option_return_code( curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, - (curl_write_callback)curlhelp_buffer_write_callback), + curlhelp_buffer_write_callback), "CURLOPT_WRITEFUNCTION"); handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&body_buf), "CURLOPT_WRITEDATA"); @@ -460,7 +461,7 @@ mp_state_enum check_http(check_curl_config config) { header_buf_initialized = true; handle_curl_option_return_code( curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, - (curl_write_callback)curlhelp_buffer_write_callback), + curlhelp_buffer_write_callback), "CURLOPT_HEADERFUNCTION"); handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&header_buf), "CURLOPT_WRITEHEADER"); @@ -488,7 +489,7 @@ mp_state_enum check_http(check_curl_config config) { char dnscache[DEFAULT_BUFFER_SIZE]; char addrstr[DEFAULT_BUFFER_SIZE / 2]; if (config.use_ssl && config.host_name != NULL) { - CURLcode res; + int res; if ((res = lookup_host(config.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2)) != 0) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), @@ -572,14 +573,15 @@ mp_state_enum check_http(check_curl_config config) { char *force_host_header = NULL; /* check if Host header is explicitly set in options */ if (config.http_opt_headers_count) { - for (int i = 0; i < config.http_opt_headers_count; i++) { + for (size_t i = 0; i < config.http_opt_headers_count; i++) { if (strncmp(config.http_opt_headers[i], "Host:", 5) == 0) { force_host_header = config.http_opt_headers[i]; } } } - /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in anyway */ + /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in + * anyway */ char http_header[DEFAULT_BUFFER_SIZE]; if (config.host_name != NULL && force_host_header == NULL) { if ((config.virtual_port != HTTP_PORT && !config.use_ssl) || @@ -599,7 +601,7 @@ mp_state_enum check_http(check_curl_config config) { /* attach additional headers supplied by the user */ /* optionally send any other header tag */ if (config.http_opt_headers_count) { - for (int i = 0; i < config.http_opt_headers_count; i++) { + for (size_t i = 0; i < config.http_opt_headers_count; i++) { header_list = curl_slist_append(header_list, config.http_opt_headers[i]); } /* This cannot be free'd here because a redirection will then try to access this and @@ -794,8 +796,8 @@ mp_state_enum check_http(check_curl_config config) { CURLOPT_POSTREDIR: method switch CURLINFO_REDIRECT_URL: custom redirect option CURLOPT_REDIRECT_PROTOCOLS: allow people to step outside safe protocols - CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range option - here is nice like for expected page size? + CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range + option here is nice like for expected page size? */ } else { /* old style redirection is handled below */ @@ -863,8 +865,8 @@ mp_state_enum check_http(check_curl_config config) { /* cookie handling */ if (config.cookie_jar_file != NULL) { - /* enable reading cookies from a file, and if the filename is an empty string, only enable - * the curl cookie engine */ + /* enable reading cookies from a file, and if the filename is an empty string, only + * enable the curl cookie engine */ handle_curl_option_return_code( curl_easy_setopt(curl, CURLOPT_COOKIEFILE, config.cookie_jar_file), "CURLOPT_COOKIEFILE"); @@ -902,7 +904,7 @@ mp_state_enum check_http(check_curl_config config) { die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } - int result_ssl = STATE_OK; + mp_state_enum result_ssl = STATE_OK; /* certificate checks */ #ifdef LIBCURL_FEATURE_SSL if (config.use_ssl) { @@ -928,8 +930,9 @@ mp_state_enum check_http(check_curl_config config) { res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &cert_ptr.to_info); if (!res && cert_ptr.to_info) { # ifdef USE_OPENSSL - /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert parsing - * We only check the first certificate and assume it's the one of the server + /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert + * parsing We only check the first certificate and assume it's the one of + * the server */ const char *raw_cert = NULL; for (int i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) { @@ -952,7 +955,7 @@ mp_state_enum check_http(check_curl_config config) { die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } BIO *cert_BIO = BIO_new(BIO_s_mem()); - BIO_write(cert_BIO, raw_cert, strlen(raw_cert)); + BIO_write(cert_BIO, raw_cert, (int)strlen(raw_cert)); cert = PEM_read_bio_X509(cert_BIO, NULL, NULL, NULL); if (!cert) { snprintf( @@ -993,7 +996,7 @@ mp_state_enum check_http(check_curl_config config) { double total_time; handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total_time), "CURLINFO_TOTAL_TIME"); - int page_len = get_content_length(&header_buf, &body_buf); + size_t page_len = get_content_length(&header_buf, &body_buf); char perfstring[DEFAULT_BUFFER_SIZE]; if (config.show_extended_perfdata) { double time_connect; @@ -1038,7 +1041,8 @@ mp_state_enum check_http(check_curl_config config) { snprintf(msg, DEFAULT_BUFFER_SIZE, "Unparsable status line in %.3g seconds response time|%s\n", total_time, perfstring); - /* we cannot know the major/minor version here for sure as we cannot parse the first line */ + /* we cannot know the major/minor version here for sure as we cannot parse the first + * line */ die(STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x %ld unknown - %s", code, msg); } status_line_initialized = true; @@ -1070,7 +1074,7 @@ mp_state_enum check_http(check_curl_config config) { config.show_body ? body_buf.buf : ""); } - int result = STATE_OK; + mp_state_enum result = STATE_OK; if (config.server_expect_yn) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), config.server_expect); @@ -1225,7 +1229,7 @@ mp_state_enum check_http(check_curl_config config) { if ((config.max_page_len > 0) && (page_len > config.max_page_len)) { char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %d too large, "), msg, page_len); + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %zu too large, "), msg, page_len); strcpy(msg, tmp); @@ -1234,7 +1238,7 @@ mp_state_enum check_http(check_curl_config config) { } else if ((config.min_page_len > 0) && (page_len < config.min_page_len)) { char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %d too small, "), msg, page_len); + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %zu too small, "), msg, page_len); strcpy(msg, tmp); result = max_state_alt(STATE_WARNING, result); } @@ -1253,8 +1257,8 @@ mp_state_enum check_http(check_curl_config config) { /* TODO: separate _() msg and status code: die (result, "HTTP %s: %s\n", state_text(result), * msg); */ - die(max_state_alt(result, result_ssl), - "HTTP %s: %s %d %s%s%s - %d bytes in %.3f second response time %s|%s\n%s%s", + die((int)max_state_alt(result, result_ssl), + "HTTP %s: %s %d %s%s%s - %zu bytes in %.3f second response time %s|%s\n%s%s", state_text(result), string_statuscode(status_line.http_major, status_line.http_minor), status_line.http_code, status_line.msg, strlen(msg) > 0 ? " - " : "", msg, page_len, total_time, (config.display_html ? "" : ""), perfstring, @@ -1582,7 +1586,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { case 'H': /* virtual host */ result.config.host_name = strdup(optarg); char *p; - int host_name_length; + size_t host_name_length; if (result.config.host_name[0] == '[') { if ((p = strstr(result.config.host_name, "]:")) != NULL) { /* [IPv6]:port */ result.config.virtual_port = atoi(p + 2); @@ -1734,31 +1738,31 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { result.config.ssl_version = CURL_SSLVERSION_SSLv2; } else if (optarg[0] == '3') { result.config.ssl_version = CURL_SSLVERSION_SSLv3; - } else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0")) + } else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0")) { # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) result.config.ssl_version = CURL_SSLVERSION_TLSv1_0; # else result.config.ssl_version = CURL_SSLVERSION_DEFAULT; # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ - else if (!strcmp(optarg, "1.1")) + } else if (!strcmp(optarg, "1.1")) { # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) result.config.ssl_version = CURL_SSLVERSION_TLSv1_1; # else result.config.ssl_version = CURL_SSLVERSION_DEFAULT; # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ - else if (!strcmp(optarg, "1.2")) + } else if (!strcmp(optarg, "1.2")) { # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) result.config.ssl_version = CURL_SSLVERSION_TLSv1_2; # else result.config.ssl_version = CURL_SSLVERSION_DEFAULT; # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ - else if (!strcmp(optarg, "1.3")) + } else if (!strcmp(optarg, "1.3")) { # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) result.config.ssl_version = CURL_SSLVERSION_TLSv1_3; # else result.config.ssl_version = CURL_SSLVERSION_DEFAULT; # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */ - else { + } else { usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 " "(with optional '+' suffix)")); } @@ -1914,7 +1918,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { printf("Bad format: try \"-m min:max\"\n"); exit(STATE_WARNING); } else { - result.config.min_page_len = atoi(tmp); + result.config.min_page_len = atol(tmp); } tmp = strtok(NULL, ":"); @@ -1922,10 +1926,10 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { printf("Bad format: try \"-m min:max\"\n"); exit(STATE_WARNING); } else { - result.config.max_page_len = atoi(tmp); + result.config.max_page_len = atol(tmp); } } else { - result.config.min_page_len = atoi(optarg); + result.config.min_page_len = atol(optarg); } break; } @@ -2042,8 +2046,8 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } char *perfd_time(double elapsed_time, thresholds *thlds, long socket_timeout) { - return fperfdata("time", elapsed_time, "s", thlds->warning, - thlds->warning ? thlds->warning->end : 0, thlds->critical, + return fperfdata("time", elapsed_time, "s", (thlds->warning != NULL), + thlds->warning ? thlds->warning->end : 0, (thlds->critical != NULL), thlds->critical ? thlds->critical->end : 0, true, 0, true, socket_timeout); } @@ -2072,7 +2076,7 @@ char *perfd_time_transfer(double elapsed_time_transfer, long socket_timeout) { true, socket_timeout); } -char *perfd_size(int page_len, int min_page_len) { +char *perfd_size(size_t page_len, int min_page_len) { return perfdata("size", page_len, "B", (min_page_len > 0), min_page_len, (min_page_len > 0), 0, true, 0, false, 0); } @@ -2120,9 +2124,8 @@ void print_help(void) { printf(" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents")); printf(" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,")); - printf( - " %s\n", - _("1.2 = TLSv1.2, 1.3 = TLSv1.3). With a '+' suffix, newer versions are also accepted.")); + printf(" %s\n", _("1.2 = TLSv1.2, 1.3 = TLSv1.3). With a '+' suffix, newer versions are " + "also accepted.")); printf(" %s\n", _("Note: SSLv2, SSLv3, TLSv1.0 and TLSv1.1 are deprecated and are usually " "disabled in libcurl")); printf(" %s\n", "--sni"); @@ -2205,9 +2208,8 @@ void print_help(void) { printf(" %s\n", "-A, --useragent=STRING"); printf(" %s\n", _("String to be sent in http header as \"User Agent\"")); printf(" %s\n", "-k, --header=STRING"); - printf( - " %s\n", - _("Any other tags to be sent in http header. Use multiple times for additional headers")); + printf(" %s\n", _("Any other tags to be sent in http header. Use multiple times for " + "additional headers")); printf(" %s\n", "-E, --extended-perfdata"); printf(" %s\n", _("Print additional performance data")); printf(" %s\n", "-B, --show-body"); @@ -2357,7 +2359,7 @@ void print_curl_version(void) { printf("%s\n", curl_version()); } int curlhelp_initwritebuffer(curlhelp_write_curlbuf *buf) { buf->bufsize = DEFAULT_BUFFER_SIZE; buf->buflen = 0; - buf->buf = (char *)malloc((size_t)buf->bufsize); + buf->buf = (char *)malloc(buf->bufsize); if (buf->buf == NULL) { return -1; } @@ -2372,7 +2374,7 @@ size_t curlhelp_buffer_write_callback(void *buffer, size_t size, size_t nmemb, v buf->buf = (char *)realloc(buf->buf, buf->bufsize); if (buf->buf == NULL) { fprintf(stderr, "malloc failed (%d) %s\n", errno, strerror(errno)); - return -1; + return 0; } } @@ -2380,7 +2382,7 @@ size_t curlhelp_buffer_write_callback(void *buffer, size_t size, size_t nmemb, v buf->buflen += size * nmemb; buf->buf[buf->buflen] = '\0'; - return (int)(size * nmemb); + return size * nmemb; } size_t curlhelp_buffer_read_callback(void *buffer, size_t size, size_t nmemb, void *stream) { @@ -2391,7 +2393,7 @@ size_t curlhelp_buffer_read_callback(void *buffer, size_t size, size_t nmemb, vo memcpy(buffer, buf->buf + buf->pos, n); buf->pos += n; - return (int)n; + return n; } void curlhelp_freewritebuffer(curlhelp_write_curlbuf *buf) { @@ -2401,7 +2403,7 @@ void curlhelp_freewritebuffer(curlhelp_write_curlbuf *buf) { int curlhelp_initreadbuffer(curlhelp_read_curlbuf *buf, const char *data, size_t datalen) { buf->buflen = datalen; - buf->buf = (char *)malloc((size_t)buf->buflen); + buf->buf = (char *)malloc(buf->buflen); if (buf->buf == NULL) { return -1; } @@ -2568,8 +2570,8 @@ char *get_header_value(const struct phr_header *headers, const size_t nof_header return NULL; } -int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[DEFAULT_BUFFER_SIZE], - int maximum_age) { +mp_state_enum check_document_dates(const curlhelp_write_curlbuf *header_buf, + char (*msg)[DEFAULT_BUFFER_SIZE], int maximum_age) { struct phr_header headers[255]; size_t nof_headers = 255; curlhelp_statusline status_line; @@ -2585,7 +2587,7 @@ int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[D char *server_date = get_header_value(headers, nof_headers, "date"); char *document_date = get_header_value(headers, nof_headers, "last-modified"); - int date_result = STATE_OK; + mp_state_enum date_result = STATE_OK; if (!server_date || !*server_date) { char tmp[DEFAULT_BUFFER_SIZE]; @@ -2634,20 +2636,20 @@ int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[D date_result = max_state_alt(STATE_CRITICAL, date_result); } else if (doc_data < srv_data - maximum_age) { - int n = (srv_data - doc_data); - if (n > (60 * 60 * 24 * 2)) { + time_t last_modified = (srv_data - doc_data); + if (last_modified > (60 * 60 * 24 * 2)) { char tmp[DEFAULT_BUFFER_SIZE]; snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), *msg, - ((float)n) / (60 * 60 * 24)); + ((float)last_modified) / (60 * 60 * 24)); strcpy(*msg, tmp); date_result = max_state_alt(STATE_CRITICAL, date_result); } else { char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %d:%02d:%02d ago, "), *msg, - n / (60 * 60), (n / 60) % 60, n % 60); + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %ld:%02ld:%02ld ago, "), *msg, + last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60); strcpy(*msg, tmp); date_result = max_state_alt(STATE_CRITICAL, date_result); @@ -2665,8 +2667,8 @@ int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[D return date_result; } -int get_content_length(const curlhelp_write_curlbuf *header_buf, - const curlhelp_write_curlbuf *body_buf) { +size_t get_content_length(const curlhelp_write_curlbuf *header_buf, + const curlhelp_write_curlbuf *body_buf) { struct phr_header headers[255]; size_t nof_headers = 255; size_t msglen; diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index 9085fbbb..90ea3810 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -48,7 +48,7 @@ typedef struct { char *http_method; char user_agent[DEFAULT_BUFFER_SIZE]; char **http_opt_headers; - int http_opt_headers_count; + size_t http_opt_headers_count; char *http_post_data; int max_depth; char *http_content_type; @@ -63,7 +63,7 @@ typedef struct { int maximum_age; char regexp[MAX_RE_SIZE]; - int state_regex; + mp_state_enum state_regex; bool invert_regex; bool verify_peer_and_host; bool check_cert; @@ -71,13 +71,13 @@ typedef struct { int days_till_exp_warn; int days_till_exp_crit; thresholds *thlds; - int min_page_len; - int max_page_len; + size_t min_page_len; + size_t max_page_len; char server_expect[MAX_INPUT_BUFFER]; bool server_expect_yn; char string_expect[MAX_INPUT_BUFFER]; char header_expect[MAX_INPUT_BUFFER]; - int onredirect; + mp_state_enum onredirect; bool show_extended_perfdata; bool show_body; @@ -100,7 +100,7 @@ check_curl_config check_curl_config_init() { .use_ssl = false, .ssl_version = CURL_SSLVERSION_DEFAULT, .http_method = NULL, - .user_agent = {}, + .user_agent = {'\0'}, .http_opt_headers = NULL, .http_opt_headers_count = 0, .http_post_data = NULL, -- cgit v1.2.3-74-g34f1 From b54869391faab7ef91586c81de21f18a61bac5aa Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 1 Aug 2025 14:29:48 +0200 Subject: Quick save --- plugins/check_curl.c | 51 ++++++++++++++++++++++--------------------- plugins/check_curl.d/config.h | 3 +++ 2 files changed, 29 insertions(+), 25 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 88c5f3e7..94b726d0 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -127,8 +127,6 @@ static curlhelp_statusline status_line; static bool put_buf_initialized = false; static curlhelp_read_curlbuf put_buf; -static struct curl_slist *server_ips = NULL; // TODO maybe unused -static int redir_depth = 0; // Maybe global static CURL *curl; static struct curl_slist *header_list = NULL; static long code; @@ -138,7 +136,6 @@ typedef union { struct curl_slist *to_info; struct curl_certinfo *to_certinfo; } cert_ptr_union; -static cert_ptr_union cert_ptr; static bool is_openssl_callback = false; static bool add_sslctx_verify_fun = false; @@ -146,7 +143,6 @@ static bool add_sslctx_verify_fun = false; static X509 *cert = NULL; #endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ -static int address_family = AF_UNSPEC; static curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN; typedef struct { @@ -156,8 +152,11 @@ typedef struct { static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); static void handle_curl_option_return_code(CURLcode res, const char *option); -static mp_state_enum check_http(check_curl_config /*config*/); -static void redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/); +static mp_state_enum check_http(check_curl_config /*config*/, int redir_depth); + +static void redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/, + int redir_depth); + static char *perfd_time(double elapsed_time, thresholds * /*thlds*/, long /*socket_timeout*/); static char *perfd_time_connect(double elapsed_time_connect, long /*socket_timeout*/); static char *perfd_time_ssl(double elapsed_time_ssl, long /*socket_timeout*/); @@ -224,7 +223,9 @@ int main(int argc, char **argv) { config.virtual_port ? config.virtual_port : config.server_port, config.server_url); } - exit((int)check_http(config)); + int redir_depth = 0; + + exit((int)check_http(config, redir_depth)); } #ifdef HAVE_SSL @@ -326,10 +327,10 @@ void handle_curl_option_return_code(CURLcode res, const char *option) { } } -int lookup_host(const char *host, char *buf, size_t buflen) { +int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family) { struct addrinfo hints; memset(&hints, 0, sizeof(hints)); - hints.ai_family = address_family; + hints.ai_family = addr_family; hints.ai_socktype = SOCK_STREAM; hints.ai_flags |= AI_CANONNAME; @@ -408,7 +409,7 @@ static void cleanup(void) { put_buf_initialized = false; } -mp_state_enum check_http(check_curl_config config) { +mp_state_enum check_http(check_curl_config config, int redir_depth) { /* initialize curl */ if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n"); @@ -448,8 +449,7 @@ mp_state_enum check_http(check_curl_config config) { } body_buf_initialized = true; handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, - curlhelp_buffer_write_callback), + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlhelp_buffer_write_callback), "CURLOPT_WRITEFUNCTION"); handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&body_buf), "CURLOPT_WRITEDATA"); @@ -460,8 +460,7 @@ mp_state_enum check_http(check_curl_config config) { } header_buf_initialized = true; handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, - curlhelp_buffer_write_callback), + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curlhelp_buffer_write_callback), "CURLOPT_HEADERFUNCTION"); handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&header_buf), "CURLOPT_WRITEHEADER"); @@ -490,7 +489,8 @@ mp_state_enum check_http(check_curl_config config) { char addrstr[DEFAULT_BUFFER_SIZE / 2]; if (config.use_ssl && config.host_name != NULL) { int res; - if ((res = lookup_host(config.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2)) != 0) { + if ((res = lookup_host(config.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2, + config.sin_family)) != 0) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), config.server_address, res, gai_strerror(res)); @@ -810,16 +810,16 @@ mp_state_enum check_http(check_curl_config config) { } /* IPv4 or IPv6 forced DNS resolution */ - if (address_family == AF_UNSPEC) { + if (config.sin_family == AF_UNSPEC) { handle_curl_option_return_code( curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)"); - } else if (address_family == AF_INET) { + } else if (config.sin_family == AF_INET) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)"); } #if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) - else if (address_family == AF_INET6) { + else if (config.sin_family == AF_INET6) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)"); } @@ -889,8 +889,7 @@ mp_state_enum check_http(check_curl_config config) { /* free header and server IP resolve lists, we don't need it anymore */ curl_slist_free_all(header_list); header_list = NULL; - curl_slist_free_all(server_ips); - server_ips = NULL; + if (host) { curl_slist_free_all(host); host = NULL; @@ -926,6 +925,7 @@ mp_state_enum check_http(check_curl_config config) { } else { struct curl_slist *slist; + cert_ptr_union cert_ptr = {0}; cert_ptr.to_info = NULL; res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &cert_ptr.to_info); if (!res && cert_ptr.to_info) { @@ -1103,7 +1103,7 @@ mp_state_enum check_http(check_curl_config config) { * back here, we are in the same status as with * the libcurl method */ - redir(&header_buf, config); + redir(&header_buf, config, redir_depth); } } else { /* this is a specific code in the command line to @@ -1287,7 +1287,8 @@ char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) { return buf; } -void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config) { +void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config, int redir_depth) { + curlhelp_statusline status_line; struct phr_header headers[255]; size_t msglen; @@ -1443,7 +1444,7 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config) { */ cleanup(); - check_http(config); + check_http(config, redir_depth); } /* check whether a file exists */ @@ -1899,11 +1900,11 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } break; case '4': - address_family = AF_INET; + result.config.sin_family = AF_INET; break; case '6': #if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) - address_family = AF_INET6; + result.config.sin_family = AF_INET6; #else usage4(_("IPv6 support not available")); #endif diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index 90ea3810..43be0306 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -6,6 +6,7 @@ #include "thresholds.h" #include #include +#include #include "curl/curl.h" enum { @@ -37,6 +38,7 @@ typedef struct { unsigned short virtual_port; char *host_name; char *server_url; + sa_family_t sin_family; bool automatic_decompression; bool haproxy_protocol; @@ -91,6 +93,7 @@ check_curl_config check_curl_config_init() { .virtual_port = 0, .host_name = NULL, .server_url = strdup(DEFAULT_SERVER_URL), + .sin_family = AF_UNSPEC, .automatic_decompression = false, .haproxy_protocol = false, -- cgit v1.2.3-74-g34f1 From 40b062f1bd77d60279555a2f40f853d829b15db6 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:12:03 +0200 Subject: check_curl: more refactoring --- plugins/check_curl.c | 787 +++++++++++++++++++++++------------------- plugins/check_curl.d/config.h | 48 ++- 2 files changed, 469 insertions(+), 366 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 94b726d0..76dcbfd8 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -115,21 +115,37 @@ enum { #include "regex.h" // Globals -static bool curl_global_initialized = false; -static bool curl_easy_initialized = false; static int verbose = 0; -static bool body_buf_initialized = false; -static curlhelp_write_curlbuf body_buf; -static bool header_buf_initialized = false; -static curlhelp_write_curlbuf header_buf; -static bool status_line_initialized = false; -static curlhelp_statusline status_line; -static bool put_buf_initialized = false; -static curlhelp_read_curlbuf put_buf; - -static CURL *curl; -static struct curl_slist *header_list = NULL; -static long code; + +typedef struct { + bool curl_global_initialized; + bool curl_easy_initialized; + bool body_buf_initialized; + curlhelp_write_curlbuf body_buf; + bool header_buf_initialized; + curlhelp_write_curlbuf header_buf; + bool status_line_initialized; + curlhelp_statusline status_line; + bool put_buf_initialized; + curlhelp_read_curlbuf put_buf; + CURL *curl; +} check_curl_global_state; + +check_curl_global_state global_state = { + .curl_global_initialized = false, + .curl_easy_initialized = false, + .body_buf_initialized = false, + .body_buf = {}, + .header_buf_initialized = false, + .header_buf = {}, + .status_line_initialized = false, + .status_line = {}, + .put_buf_initialized = false, + .put_buf = {}, + .curl = NULL, +}; + +static long httpReturnCode; static char errbuf[MAX_INPUT_BUFFER]; static char msg[DEFAULT_BUFFER_SIZE]; typedef union { @@ -143,8 +159,6 @@ static bool add_sslctx_verify_fun = false; static X509 *cert = NULL; #endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ -static curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN; - typedef struct { int errorcode; check_curl_config config; @@ -152,10 +166,16 @@ typedef struct { static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); static void handle_curl_option_return_code(CURLcode res, const char *option); -static mp_state_enum check_http(check_curl_config /*config*/, int redir_depth); +static mp_state_enum check_http(check_curl_config /*config*/, check_curl_working_state workingState, + int redir_depth, struct curl_slist *header_list); -static void redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/, - int redir_depth); +typedef struct { + int redir_depth; + check_curl_working_state working_state; + int error_code; +} redir_wrapper; +static redir_wrapper redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/, + int redir_depth, check_curl_working_state working_state); static char *perfd_time(double elapsed_time, thresholds * /*thlds*/, long /*socket_timeout*/); static char *perfd_time_connect(double elapsed_time_connect, long /*socket_timeout*/); @@ -218,14 +238,21 @@ int main(int argc, char **argv) { } if (config.display_html) { - printf("", config.use_ssl ? "https" : "http", - config.host_name ? config.host_name : config.server_address, - config.virtual_port ? config.virtual_port : config.server_port, config.server_url); + printf("", + config.initial_config.use_ssl ? "https" : "http", + config.initial_config.host_name ? config.initial_config.host_name + : config.initial_config.server_address, + config.initial_config.virtualPort ? config.initial_config.virtualPort + : config.initial_config.serverPort, + config.initial_config.server_url); } int redir_depth = 0; - exit((int)check_http(config, redir_depth)); + check_curl_working_state working_state = config.initial_config; + struct curl_slist *header_list = NULL; + + exit((int)check_http(config, working_state, redir_depth, header_list)); } #ifdef HAVE_SSL @@ -243,13 +270,11 @@ int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { # endif if (verbose >= 2) { puts("* SSL verify callback with certificate:"); - X509_NAME *subject; - X509_NAME *issuer; printf("* issuer:\n"); - issuer = X509_get_issuer_name(cert); + X509_NAME *issuer = X509_get_issuer_name(cert); X509_NAME_print_ex_fp(stdout, issuer, 5, XN_FLAG_MULTILINE); printf("* curl verify_callback:\n* subject:\n"); - subject = X509_get_subject_name(cert); + X509_NAME *subject = X509_get_subject_name(cert); X509_NAME_print_ex_fp(stdout, subject, 5, XN_FLAG_MULTILINE); puts(""); } @@ -305,9 +330,8 @@ static bool expected_statuscode(const char *reply, const char *statuscodes) { die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); } - char *code; bool result = false; - for (code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) { + for (char *code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) { if (strstr(reply, code) != NULL) { result = true; break; @@ -328,11 +352,11 @@ void handle_curl_option_return_code(CURLcode res, const char *option) { } int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family) { - struct addrinfo hints; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = addr_family; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags |= AI_CANONNAME; + struct addrinfo hints = { + .ai_family = addr_family, + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_CANONNAME, + }; struct addrinfo *result; int errcode = getaddrinfo(host, NULL, &hints, &result); @@ -383,103 +407,115 @@ int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_fam } static void cleanup(void) { - if (status_line_initialized) { - curlhelp_free_statusline(&status_line); + if (global_state.status_line_initialized) { + curlhelp_free_statusline(&global_state.status_line); } - status_line_initialized = false; - if (curl_easy_initialized) { - curl_easy_cleanup(curl); + global_state.status_line_initialized = false; + + if (global_state.curl_easy_initialized) { + curl_easy_cleanup(global_state.curl); } - curl_easy_initialized = false; - if (curl_global_initialized) { + global_state.curl_easy_initialized = false; + + if (global_state.curl_global_initialized) { curl_global_cleanup(); } - curl_global_initialized = false; - if (body_buf_initialized) { - curlhelp_freewritebuffer(&body_buf); + global_state.curl_global_initialized = false; + + if (global_state.body_buf_initialized) { + curlhelp_freewritebuffer(&global_state.body_buf); } - body_buf_initialized = false; - if (header_buf_initialized) { - curlhelp_freewritebuffer(&header_buf); + global_state.body_buf_initialized = false; + + if (global_state.header_buf_initialized) { + curlhelp_freewritebuffer(&global_state.header_buf); } - header_buf_initialized = false; - if (put_buf_initialized) { - curlhelp_freereadbuffer(&put_buf); + global_state.header_buf_initialized = false; + + if (global_state.put_buf_initialized) { + curlhelp_freereadbuffer(&global_state.put_buf); } - put_buf_initialized = false; + global_state.put_buf_initialized = false; } -mp_state_enum check_http(check_curl_config config, int redir_depth) { +mp_state_enum check_http(const check_curl_config config, check_curl_working_state workingState, + int redir_depth, struct curl_slist *header_list) { /* initialize curl */ if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n"); } - curl_global_initialized = true; + global_state.curl_global_initialized = true; - if ((curl = curl_easy_init()) == NULL) { + if ((global_state.curl = curl_easy_init()) == NULL) { die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n"); } - curl_easy_initialized = true; + global_state.curl_easy_initialized = true; /* register cleanup function to shut down libcurl properly */ atexit(cleanup); if (verbose >= 1) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_VERBOSE, 1), + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_VERBOSE, 1), "CURLOPT_VERBOSE"); } /* print everything on stdout like check_http would do */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_STDERR, stdout), + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_STDERR, stdout), "CURLOPT_STDERR"); if (config.automatic_decompression) { #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""), - "CURLOPT_ACCEPT_ENCODING"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_ACCEPT_ENCODING, ""), + "CURLOPT_ACCEPT_ENCODING"); #else - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ENCODING, ""), + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING"); #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */ } /* initialize buffer for body of the answer */ - if (curlhelp_initwritebuffer(&body_buf) < 0) { + if (curlhelp_initwritebuffer(&global_state.body_buf) < 0) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n"); } - body_buf_initialized = true; + global_state.body_buf_initialized = true; handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlhelp_buffer_write_callback), + curl_easy_setopt(global_state.curl, CURLOPT_WRITEFUNCTION, curlhelp_buffer_write_callback), "CURLOPT_WRITEFUNCTION"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&body_buf), - "CURLOPT_WRITEDATA"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_WRITEDATA, (void *)&global_state.body_buf), + "CURLOPT_WRITEDATA"); /* initialize buffer for header of the answer */ - if (curlhelp_initwritebuffer(&header_buf) < 0) { + if (curlhelp_initwritebuffer(&global_state.header_buf) < 0) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n"); } - header_buf_initialized = true; + global_state.header_buf_initialized = true; + handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curlhelp_buffer_write_callback), + curl_easy_setopt(global_state.curl, CURLOPT_HEADERFUNCTION, curlhelp_buffer_write_callback), "CURLOPT_HEADERFUNCTION"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&header_buf), - "CURLOPT_WRITEHEADER"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_WRITEHEADER, (void *)&global_state.header_buf), + "CURLOPT_WRITEHEADER"); /* set the error buffer */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf), + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_ERRORBUFFER, errbuf), "CURLOPT_ERRORBUFFER"); /* set timeouts */ handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, config.socket_timeout), + curl_easy_setopt(global_state.curl, CURLOPT_CONNECTTIMEOUT, config.socket_timeout), "CURLOPT_CONNECTTIMEOUT"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_TIMEOUT, config.socket_timeout), - "CURLOPT_TIMEOUT"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_TIMEOUT, config.socket_timeout), + "CURLOPT_TIMEOUT"); /* enable haproxy protocol */ if (config.haproxy_protocol) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HAPROXYPROTOCOL, 1L), - "CURLOPT_HAPROXYPROTOCOL"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_HAPROXYPROTOCOL, 1L), + "CURLOPT_HAPROXYPROTOCOL"); } // fill dns resolve cache to make curl connect to the given server_address instead of the @@ -487,19 +523,19 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { struct curl_slist *host = NULL; char dnscache[DEFAULT_BUFFER_SIZE]; char addrstr[DEFAULT_BUFFER_SIZE / 2]; - if (config.use_ssl && config.host_name != NULL) { + if (workingState.use_ssl && workingState.host_name != NULL) { int res; - if ((res = lookup_host(config.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2, + if ((res = lookup_host(workingState.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2, config.sin_family)) != 0) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), - config.server_address, res, gai_strerror(res)); + workingState.server_address, res, gai_strerror(res)); die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } - snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", config.host_name, config.server_port, - addrstr); + snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", workingState.host_name, + workingState.serverPort, addrstr); host = curl_slist_append(NULL, dnscache); - curl_easy_setopt(curl, CURLOPT_RESOLVE, host); + curl_easy_setopt(global_state.curl, CURLOPT_RESOLVE, host); if (verbose >= 1) { printf("* curl CURLOPT_RESOLVE: %s\n", dnscache); } @@ -507,66 +543,73 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { // If server_address is an IPv6 address it must be surround by square brackets struct in6_addr tmp_in_addr; - if (inet_pton(AF_INET6, config.server_address, &tmp_in_addr) == 1) { - char *new_server_address = malloc(strlen(config.server_address) + 3); + if (inet_pton(AF_INET6, workingState.server_address, &tmp_in_addr) == 1) { + char *new_server_address = malloc(strlen(workingState.server_address) + 3); if (new_server_address == NULL) { die(STATE_UNKNOWN, "HTTP UNKNOWN - Unable to allocate memory\n"); } - snprintf(new_server_address, strlen(config.server_address) + 3, "[%s]", - config.server_address); - free(config.server_address); - config.server_address = new_server_address; + snprintf(new_server_address, strlen(workingState.server_address) + 3, "[%s]", + workingState.server_address); + free(workingState.server_address); + workingState.server_address = new_server_address; } /* compose URL: use the address we want to connect to, set Host: header later */ char url[DEFAULT_BUFFER_SIZE]; - snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", config.use_ssl ? "https" : "http", - (config.use_ssl & (config.host_name != NULL)) ? config.host_name - : config.server_address, - config.server_port, config.server_url); + snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", workingState.use_ssl ? "https" : "http", + (workingState.use_ssl & (workingState.host_name != NULL)) + ? workingState.host_name + : workingState.server_address, + workingState.serverPort, workingState.server_url); if (verbose >= 1) { printf("* curl CURLOPT_URL: %s\n", url); } - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, url), "CURLOPT_URL"); + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_URL, url), + "CURLOPT_URL"); /* extract proxy information for legacy proxy https requests */ - if (!strcmp(config.http_method, "CONNECT") || - strstr(config.server_url, "http") == config.server_url) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXY, config.server_address), - "CURLOPT_PROXY"); + if (!strcmp(workingState.http_method, "CONNECT") || + strstr(workingState.server_url, "http") == workingState.server_url) { + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_PROXY, workingState.server_address), + "CURLOPT_PROXY"); handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_PROXYPORT, (long)config.server_port), + curl_easy_setopt(global_state.curl, CURLOPT_PROXYPORT, (long)workingState.serverPort), "CURLOPT_PROXYPORT"); if (verbose >= 2) { - printf("* curl CURLOPT_PROXY: %s:%d\n", config.server_address, config.server_port); + printf("* curl CURLOPT_PROXY: %s:%d\n", workingState.server_address, + workingState.serverPort); } - config.http_method = "GET"; - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, config.server_url), - "CURLOPT_URL"); + workingState.http_method = "GET"; + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_URL, workingState.server_url), + "CURLOPT_URL"); } /* disable body for HEAD request */ - if (config.http_method && !strcmp(config.http_method, "HEAD")) { - config.no_body = true; + if (workingState.http_method && !strcmp(workingState.http_method, "HEAD")) { + workingState.no_body = true; } /* set HTTP protocol version */ handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, config.curl_http_version), + curl_easy_setopt(global_state.curl, CURLOPT_HTTP_VERSION, config.curl_http_version), "CURLOPT_HTTP_VERSION"); /* set HTTP method */ - if (config.http_method) { - if (!strcmp(config.http_method, "POST")) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POST, 1), "CURLOPT_POST"); - } else if (!strcmp(config.http_method, "PUT")) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_UPLOAD, 1), + if (workingState.http_method) { + if (!strcmp(workingState.http_method, "POST")) { + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_POST, 1), + "CURLOPT_POST"); + } else if (!strcmp(workingState.http_method, "PUT")) { + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD"); } else { - handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, config.http_method), - "CURLOPT_CUSTOMREQUEST"); + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, + CURLOPT_CUSTOMREQUEST, + workingState.http_method), + "CURLOPT_CUSTOMREQUEST"); } } @@ -583,13 +626,13 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in * anyway */ char http_header[DEFAULT_BUFFER_SIZE]; - if (config.host_name != NULL && force_host_header == NULL) { - if ((config.virtual_port != HTTP_PORT && !config.use_ssl) || - (config.virtual_port != HTTPS_PORT && config.use_ssl)) { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", config.host_name, - config.virtual_port); + if (workingState.host_name != NULL && force_host_header == NULL) { + if ((workingState.virtualPort != HTTP_PORT && !workingState.use_ssl) || + (workingState.virtualPort != HTTPS_PORT && workingState.use_ssl)) { + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", workingState.host_name, + workingState.virtualPort); } else { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", config.host_name); + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", workingState.host_name); } header_list = curl_slist_append(header_list, http_header); } @@ -604,57 +647,63 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { for (size_t i = 0; i < config.http_opt_headers_count; i++) { header_list = curl_slist_append(header_list, config.http_opt_headers[i]); } - /* This cannot be free'd here because a redirection will then try to access this and - * segfault */ - /* Covered in a testcase in tests/check_http.t */ - /* free(http_opt_headers); */ } /* set HTTP headers */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list), - "CURLOPT_HTTPHEADER"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_HTTPHEADER, header_list), "CURLOPT_HTTPHEADER"); #ifdef LIBCURL_FEATURE_SSL /* set SSL version, warn about insecure or unsupported versions */ - if (config.use_ssl) { + if (workingState.use_ssl) { handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_SSLVERSION, config.ssl_version), "CURLOPT_SSLVERSION"); + curl_easy_setopt(global_state.curl, CURLOPT_SSLVERSION, config.ssl_version), + "CURLOPT_SSLVERSION"); } /* client certificate and key to present to server (SSL) */ if (config.client_cert) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLCERT, config.client_cert), - "CURLOPT_SSLCERT"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_SSLCERT, config.client_cert), + "CURLOPT_SSLCERT"); } + if (config.client_privkey) { handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_SSLKEY, config.client_privkey), "CURLOPT_SSLKEY"); + curl_easy_setopt(global_state.curl, CURLOPT_SSLKEY, config.client_privkey), + "CURLOPT_SSLKEY"); } + if (config.ca_cert) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CAINFO, config.ca_cert), - "CURLOPT_CAINFO"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_CAINFO, config.ca_cert), "CURLOPT_CAINFO"); } + if (config.ca_cert || config.verify_peer_and_host) { /* per default if we have a CA verify both the peer and the * hostname in the certificate, can be switched off later */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1), - "CURLOPT_SSL_VERIFYPEER"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2), - "CURLOPT_SSL_VERIFYHOST"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_SSL_VERIFYPEER, 1), + "CURLOPT_SSL_VERIFYPEER"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_SSL_VERIFYHOST, 2), + "CURLOPT_SSL_VERIFYHOST"); } else { /* backward-compatible behaviour, be tolerant in checks * TODO: depending on more options have aspects we want * to be less tolerant about ssl verfications */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0), - "CURLOPT_SSL_VERIFYPEER"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0), - "CURLOPT_SSL_VERIFYHOST"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_SSL_VERIFYPEER, 0), + "CURLOPT_SSL_VERIFYPEER"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_SSL_VERIFYHOST, 0), + "CURLOPT_SSL_VERIFYHOST"); } /* detect SSL library used by libcurl */ - ssl_library = curlhelp_get_ssl_library(); + curlhelp_ssl_library ssl_library = curlhelp_get_ssl_library(); /* try hard to get a stack of certificates to verify against */ if (config.check_cert) { @@ -672,15 +721,15 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { # endif /* USE_OPENSSL */ /* libcurl is built with OpenSSL, monitoring plugins, so falling * back to manually extracting certificate information */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), - "CURLOPT_CERTINFO"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); break; case CURLHELP_SSL_LIBRARY_NSS: # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) /* NSS: support for CERTINFO is implemented since 7.34.0 */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), - "CURLOPT_CERTINFO"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); # else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library " @@ -692,8 +741,8 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { case CURLHELP_SSL_LIBRARY_GNUTLS: # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), - "CURLOPT_CERTINFO"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); # else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */ die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library " @@ -726,29 +775,33 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { # if LIBCURL_VERSION_NUM >= \ MAKE_LIBCURL_VERSION(7, 10, 6) /* required for CURLOPT_SSL_CTX_FUNCTION */ // ssl ctx function is not available with all ssl backends - if (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, NULL) != CURLE_UNKNOWN_OPTION) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), - "CURLOPT_SSL_CTX_FUNCTION"); + if (curl_easy_setopt(global_state.curl, CURLOPT_SSL_CTX_FUNCTION, NULL) != + CURLE_UNKNOWN_OPTION) { + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), + "CURLOPT_SSL_CTX_FUNCTION"); } # endif #endif /* LIBCURL_FEATURE_SSL */ /* set default or user-given user agent identification */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERAGENT, config.user_agent), - "CURLOPT_USERAGENT"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_USERAGENT, config.user_agent), + "CURLOPT_USERAGENT"); /* proxy-authentication */ if (strcmp(config.proxy_auth, "")) { handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, config.proxy_auth), + curl_easy_setopt(global_state.curl, CURLOPT_PROXYUSERPWD, config.proxy_auth), "CURLOPT_PROXYUSERPWD"); } /* authentication */ if (strcmp(config.user_auth, "")) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERPWD, config.user_auth), - "CURLOPT_USERPWD"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_USERPWD, config.user_auth), + "CURLOPT_USERPWD"); } /* TODO: parameter auth method, bitfield of following methods: @@ -770,25 +823,27 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { /* handle redirections */ if (config.onredirect == STATE_DEPENDENT) { if (config.followmethod == FOLLOW_LIBCURL) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1), - "CURLOPT_FOLLOWLOCATION"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_FOLLOWLOCATION, 1), + "CURLOPT_FOLLOWLOCATION"); /* default -1 is infinite, not good, could lead to zombie plugins! Setting it to one bigger than maximal limit to handle errors nicely below */ handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_MAXREDIRS, config.max_depth + 1), + curl_easy_setopt(global_state.curl, CURLOPT_MAXREDIRS, config.max_depth + 1), "CURLOPT_MAXREDIRS"); /* for now allow only http and https (we are a http(s) check plugin in the end) */ #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 85, 0) handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"), + curl_easy_setopt(global_state.curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"), "CURLOPT_REDIR_PROTOCOLS_STR"); #elif LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4) - handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS), - "CURLOPT_REDIRECT_PROTOCOLS"); + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, + CURLOPT_REDIR_PROTOCOLS, + CURLPROTO_HTTP | CURLPROTO_HTTPS), + "CURLOPT_REDIRECT_PROTOCOLS"); #endif /* TODO: handle the following aspects of redirection, make them @@ -805,28 +860,31 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { } /* no-body */ - if (config.no_body) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_NOBODY, 1), "CURLOPT_NOBODY"); + if (workingState.no_body) { + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_NOBODY, 1), + "CURLOPT_NOBODY"); } /* IPv4 or IPv6 forced DNS resolution */ if (config.sin_family == AF_UNSPEC) { handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), + curl_easy_setopt(global_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)"); } else if (config.sin_family == AF_INET) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), - "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), + "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)"); } #if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) else if (config.sin_family == AF_INET6) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), - "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), + "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)"); } #endif /* either send http POST data (any data, not only POST)*/ - if (!strcmp(config.http_method, "POST") || !strcmp(config.http_method, "PUT")) { + if (!strcmp(workingState.http_method, "POST") || !strcmp(workingState.http_method, "PUT")) { /* set content of payload for POST and PUT */ if (config.http_content_type) { snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", @@ -835,30 +893,32 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { } /* NULL indicates "HTTP Continue" in libcurl, provide an empty string * in case of no POST/PUT data */ - if (!config.http_post_data) { - config.http_post_data = ""; + if (!workingState.http_post_data) { + workingState.http_post_data = ""; } - if (!strcmp(config.http_method, "POST")) { + if (!strcmp(workingState.http_method, "POST")) { /* POST method, set payload with CURLOPT_POSTFIELDS */ + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_POSTFIELDS, + workingState.http_post_data), + "CURLOPT_POSTFIELDS"); + } else if (!strcmp(workingState.http_method, "PUT")) { handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, config.http_post_data), - "CURLOPT_POSTFIELDS"); - } else if (!strcmp(config.http_method, "PUT")) { - handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_READFUNCTION, + curl_easy_setopt(global_state.curl, CURLOPT_READFUNCTION, (curl_read_callback)curlhelp_buffer_read_callback), "CURLOPT_READFUNCTION"); - if (curlhelp_initreadbuffer(&put_buf, config.http_post_data, - strlen(config.http_post_data)) < 0) { + if (curlhelp_initreadbuffer(&global_state.put_buf, workingState.http_post_data, + strlen(workingState.http_post_data)) < 0) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating read buffer for PUT\n"); } - put_buf_initialized = true; - handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_READDATA, (void *)&put_buf), "CURLOPT_READDATA"); + global_state.put_buf_initialized = true; + + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_READDATA, + (void *)&global_state.put_buf), + "CURLOPT_READDATA"); handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_INFILESIZE, - (curl_off_t)strlen(config.http_post_data)), + curl_easy_setopt(global_state.curl, CURLOPT_INFILESIZE, + (curl_off_t)strlen(workingState.http_post_data)), "CURLOPT_INFILESIZE"); } } @@ -868,22 +928,22 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { /* enable reading cookies from a file, and if the filename is an empty string, only * enable the curl cookie engine */ handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_COOKIEFILE, config.cookie_jar_file), + curl_easy_setopt(global_state.curl, CURLOPT_COOKIEFILE, config.cookie_jar_file), "CURLOPT_COOKIEFILE"); /* now enable saving cookies to a file, but only if the filename is not an empty string, * since writing it would fail */ if (*config.cookie_jar_file) { handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_COOKIEJAR, config.cookie_jar_file), + curl_easy_setopt(global_state.curl, CURLOPT_COOKIEJAR, config.cookie_jar_file), "CURLOPT_COOKIEJAR"); } } /* do the request */ - CURLcode res = curl_easy_perform(curl); + CURLcode res = curl_easy_perform(global_state.curl); - if (verbose >= 2 && config.http_post_data) { - printf("**** REQUEST CONTENT ****\n%s\n", config.http_post_data); + if (verbose >= 2 && workingState.http_post_data) { + printf("**** REQUEST CONTENT ****\n%s\n", workingState.http_post_data); } /* free header and server IP resolve lists, we don't need it anymore */ @@ -899,14 +959,14 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { if (res != CURLE_OK) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"), - config.server_port, res, errbuf[0] ? errbuf : curl_easy_strerror(res)); + workingState.serverPort, res, errbuf[0] ? errbuf : curl_easy_strerror(res)); die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } mp_state_enum result_ssl = STATE_OK; /* certificate checks */ #ifdef LIBCURL_FEATURE_SSL - if (config.use_ssl) { + if (workingState.use_ssl) { if (config.check_cert) { if (is_openssl_callback) { # ifdef USE_OPENSSL @@ -927,7 +987,7 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { cert_ptr_union cert_ptr = {0}; cert_ptr.to_info = NULL; - res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &cert_ptr.to_info); + res = curl_easy_getinfo(global_state.curl, CURLINFO_CERTINFO, &cert_ptr.to_info); if (!res && cert_ptr.to_info) { # ifdef USE_OPENSSL /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert @@ -994,32 +1054,37 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { * performance data to the answer always */ double total_time; - handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total_time), - "CURLINFO_TOTAL_TIME"); - size_t page_len = get_content_length(&header_buf, &body_buf); + handle_curl_option_return_code( + curl_easy_getinfo(global_state.curl, CURLINFO_TOTAL_TIME, &total_time), + "CURLINFO_TOTAL_TIME"); + size_t page_len = get_content_length(&global_state.header_buf, &global_state.body_buf); char perfstring[DEFAULT_BUFFER_SIZE]; if (config.show_extended_perfdata) { double time_connect; handle_curl_option_return_code( - curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &time_connect), "CURLINFO_CONNECT_TIME"); + curl_easy_getinfo(global_state.curl, CURLINFO_CONNECT_TIME, &time_connect), + "CURLINFO_CONNECT_TIME"); + double time_appconnect; handle_curl_option_return_code( - curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), + curl_easy_getinfo(global_state.curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), "CURLINFO_APPCONNECT_TIME"); + double time_headers; handle_curl_option_return_code( - curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, &time_headers), + curl_easy_getinfo(global_state.curl, CURLINFO_PRETRANSFER_TIME, &time_headers), "CURLINFO_PRETRANSFER_TIME"); + double time_firstbyte; handle_curl_option_return_code( - curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), + curl_easy_getinfo(global_state.curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), "CURLINFO_STARTTRANSFER_TIME"); snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s", perfd_time(total_time, config.thlds, config.socket_timeout), perfd_size(page_len, config.min_page_len), perfd_time_connect(time_connect, config.socket_timeout), - config.use_ssl + workingState.use_ssl ? perfd_time_ssl(time_appconnect - time_connect, config.socket_timeout) : "", perfd_time_headers(time_headers - time_appconnect, config.socket_timeout), @@ -1032,46 +1097,47 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { } /* return a CRITICAL status if we couldn't read any data */ - if (strlen(header_buf.buf) == 0 && strlen(body_buf.buf) == 0) { + if (strlen(global_state.header_buf.buf) == 0 && strlen(global_state.body_buf.buf) == 0) { die(STATE_CRITICAL, _("HTTP CRITICAL - No header received from host\n")); } /* get status line of answer, check sanity of HTTP code */ - if (curlhelp_parse_statusline(header_buf.buf, &status_line) < 0) { + if (curlhelp_parse_statusline(global_state.header_buf.buf, &global_state.status_line) < 0) { snprintf(msg, DEFAULT_BUFFER_SIZE, "Unparsable status line in %.3g seconds response time|%s\n", total_time, perfstring); /* we cannot know the major/minor version here for sure as we cannot parse the first * line */ - die(STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x %ld unknown - %s", code, msg); + die(STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x %ld unknown - %s", httpReturnCode, msg); } - status_line_initialized = true; + global_state.status_line_initialized = true; /* get result code from cURL */ - handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code), - "CURLINFO_RESPONSE_CODE"); + handle_curl_option_return_code( + curl_easy_getinfo(global_state.curl, CURLINFO_RESPONSE_CODE, &httpReturnCode), + "CURLINFO_RESPONSE_CODE"); if (verbose >= 2) { - printf("* curl CURLINFO_RESPONSE_CODE is %ld\n", code); + printf("* curl CURLINFO_RESPONSE_CODE is %ld\n", httpReturnCode); } /* print status line, header, body if verbose */ if (verbose >= 2) { - printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header_buf.buf, - (config.no_body ? " [[ skipped ]]" : body_buf.buf)); + printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", global_state.header_buf.buf, + (workingState.no_body ? " [[ skipped ]]" : global_state.body_buf.buf)); } /* make sure the status line matches the response we are looking for */ - if (!expected_statuscode(status_line.first_line, config.server_expect)) { - if (config.server_port == HTTP_PORT) { + if (!expected_statuscode(global_state.status_line.first_line, config.server_expect)) { + if (workingState.serverPort == HTTP_PORT) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), - status_line.first_line); + global_state.status_line.first_line); } else { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), - config.server_port, status_line.first_line); + workingState.serverPort, global_state.status_line.first_line); } die(STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg, config.show_body ? "\n" : "", - config.show_body ? body_buf.buf : ""); + config.show_body ? global_state.body_buf.buf : ""); } mp_state_enum result = STATE_OK; @@ -1084,26 +1150,29 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { result = STATE_OK; } else { /* illegal return codes result in a critical state */ - if (code >= 600 || code < 100) { + if (httpReturnCode >= 600 || httpReturnCode < 100) { die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), - status_line.http_code, status_line.msg); + global_state.status_line.http_code, global_state.status_line.msg); /* server errors result in a critical state */ - } else if (code >= 500) { + } else if (httpReturnCode >= 500) { result = STATE_CRITICAL; /* client errors result in a warning state */ - } else if (code >= 400) { + } else if (httpReturnCode >= 400) { result = STATE_WARNING; /* check redirected page if specified */ - } else if (code >= 300) { + } else if (httpReturnCode >= 300) { if (config.onredirect == STATE_DEPENDENT) { if (config.followmethod == FOLLOW_LIBCURL) { - code = status_line.http_code; + httpReturnCode = global_state.status_line.http_code; } else { /* old check_http style redirection, if we come * back here, we are in the same status as with * the libcurl method */ - redir(&header_buf, config, redir_depth); + redir_wrapper redir_result = + redir(&global_state.header_buf, config, redir_depth, workingState); + check_http(config, redir_result.working_state, redir_result.redir_depth, + header_list); } } else { /* this is a specific code in the command line to @@ -1120,7 +1189,7 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { /* libcurl redirection internally, handle error states here */ if (config.followmethod == FOLLOW_LIBCURL) { handle_curl_option_return_code( - curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &redir_depth), + curl_easy_getinfo(global_state.curl, CURLINFO_REDIRECT_COUNT, &redir_depth), "CURLINFO_REDIRECT_COUNT"); if (verbose >= 2) { printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth); @@ -1133,20 +1202,22 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { } /* check status codes, set exit status accordingly */ - if (status_line.http_code != code) { + if (global_state.status_line.http_code != httpReturnCode) { die(STATE_CRITICAL, _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"), - string_statuscode(status_line.http_major, status_line.http_minor), - status_line.http_code, status_line.msg, code); + string_statuscode(global_state.status_line.http_major, + global_state.status_line.http_minor), + global_state.status_line.http_code, global_state.status_line.msg, httpReturnCode); } if (config.maximum_age >= 0) { - result = max_state_alt(check_document_dates(&header_buf, &msg, config.maximum_age), result); + result = max_state_alt( + check_document_dates(&global_state.header_buf, &msg, config.maximum_age), result); } /* Page and Header content checks go here */ if (strlen(config.header_expect)) { - if (!strstr(header_buf.buf, config.header_expect)) { + if (!strstr(global_state.header_buf.buf, config.header_expect)) { char output_header_search[30] = ""; strncpy(&output_header_search[0], config.header_expect, sizeof(output_header_search)); @@ -1158,9 +1229,9 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { char tmp[DEFAULT_BUFFER_SIZE]; snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sheader '%s' not found on '%s://%s:%d%s', "), - msg, output_header_search, config.use_ssl ? "https" : "http", - config.host_name ? config.host_name : config.server_address, - config.server_port, config.server_url); + msg, output_header_search, workingState.use_ssl ? "https" : "http", + workingState.host_name ? workingState.host_name : workingState.server_address, + workingState.serverPort, workingState.server_url); strcpy(msg, tmp); @@ -1169,7 +1240,7 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { } if (strlen(config.string_expect)) { - if (!strstr(body_buf.buf, config.string_expect)) { + if (!strstr(global_state.body_buf.buf, config.string_expect)) { char output_string_search[30] = ""; strncpy(&output_string_search[0], config.string_expect, sizeof(output_string_search)); @@ -1181,9 +1252,9 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { char tmp[DEFAULT_BUFFER_SIZE]; snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sstring '%s' not found on '%s://%s:%d%s', "), - msg, output_string_search, config.use_ssl ? "https" : "http", - config.host_name ? config.host_name : config.server_address, - config.server_port, config.server_url); + msg, output_string_search, workingState.use_ssl ? "https" : "http", + workingState.host_name ? workingState.host_name : workingState.server_address, + workingState.serverPort, workingState.server_url); strcpy(msg, tmp); @@ -1194,7 +1265,7 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { if (strlen(config.regexp)) { regex_t preg; regmatch_t pmatch[REGS]; - int errcode = regexec(&preg, body_buf.buf, REGS, pmatch, 0); + int errcode = regexec(&preg, global_state.body_buf.buf, REGS, pmatch, 0); if ((errcode == 0 && !config.invert_regex) || (errcode == REG_NOMATCH && config.invert_regex)) { /* OK - No-op to avoid changing the logic around it */ @@ -1259,22 +1330,25 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { * msg); */ die((int)max_state_alt(result, result_ssl), "HTTP %s: %s %d %s%s%s - %zu bytes in %.3f second response time %s|%s\n%s%s", - state_text(result), string_statuscode(status_line.http_major, status_line.http_minor), - status_line.http_code, status_line.msg, strlen(msg) > 0 ? " - " : "", msg, page_len, - total_time, (config.display_html ? "" : ""), perfstring, - (config.show_body ? body_buf.buf : ""), (config.show_body ? "\n" : "")); + state_text(result), + string_statuscode(global_state.status_line.http_major, global_state.status_line.http_minor), + global_state.status_line.http_code, global_state.status_line.msg, + strlen(msg) > 0 ? " - " : "", msg, page_len, total_time, + (config.display_html ? "" : ""), perfstring, + (config.show_body ? global_state.body_buf.buf : ""), (config.show_body ? "\n" : "")); return max_state_alt(result, result_ssl); } -int uri_strcmp(const UriTextRangeA range, const char *s) { +int uri_strcmp(const UriTextRangeA range, const char *stringToCompare) { if (!range.first) { return -1; } - if ((size_t)(range.afterLast - range.first) < strlen(s)) { + if ((size_t)(range.afterLast - range.first) < strlen(stringToCompare)) { return -1; } - return strncmp(s, range.first, min((size_t)(range.afterLast - range.first), strlen(s))); + return strncmp(stringToCompare, range.first, + min((size_t)(range.afterLast - range.first), strlen(stringToCompare))); } char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) { @@ -1287,8 +1361,8 @@ char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) { return buf; } -void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config, int redir_depth) { - +redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config config, + int redir_depth, check_curl_working_state working_state) { curlhelp_statusline status_line; struct phr_header headers[255]; size_t msglen; @@ -1340,9 +1414,9 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config, int red } if (uri.pathHead) { printf(_("** path: ")); - const UriPathSegmentA *p = uri.pathHead; - for (; p; p = p->next) { - printf("/%s", uri_string(p->text, buf, DEFAULT_BUFFER_SIZE)); + for (UriPathSegmentA *path_segment = uri.pathHead; path_segment; + path_segment = path_segment->next) { + printf("/%s", uri_string(path_segment->text, buf, DEFAULT_BUFFER_SIZE)); } puts(""); } @@ -1355,7 +1429,7 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config, int red } if (uri.scheme.first) { - config.use_ssl = (bool)(!uri_strcmp(uri.scheme, "https")); + working_state.use_ssl = (bool)(!uri_strcmp(uri.scheme, "https")); } /* we do a sloppy test here only, because uriparser would have failed @@ -1366,7 +1440,7 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config, int red new_port = atoi(uri_string(uri.portText, buf, DEFAULT_BUFFER_SIZE)); } else { new_port = HTTP_PORT; - if (config.use_ssl) { + if (working_state.use_ssl) { new_port = HTTPS_PORT; } } @@ -1380,9 +1454,10 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config, int red */ char *new_host; if (!uri.scheme.first && !uri.hostText.first) { - new_host = strdup(config.host_name ? config.host_name : config.server_address); - new_port = config.server_port; - if (config.use_ssl) { + new_host = strdup(working_state.host_name ? working_state.host_name + : working_state.server_address); + new_port = working_state.serverPort; + if (working_state.use_ssl) { uri_string(uri.scheme, "https", DEFAULT_BUFFER_SIZE); } } else { @@ -1393,50 +1468,51 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config, int red /* TODO: handle fragments and query part of URL */ char *new_url = (char *)calloc(1, DEFAULT_BUFFER_SIZE); if (uri.pathHead) { - const UriPathSegmentA *p = uri.pathHead; - for (; p; p = p->next) { + for (UriPathSegmentA *pathSegment = uri.pathHead; pathSegment; + pathSegment = pathSegment->next) { strncat(new_url, "/", DEFAULT_BUFFER_SIZE); - strncat(new_url, uri_string(p->text, buf, DEFAULT_BUFFER_SIZE), + strncat(new_url, uri_string(pathSegment->text, buf, DEFAULT_BUFFER_SIZE), DEFAULT_BUFFER_SIZE - 1); } } - if (config.server_port == new_port && - !strncmp(config.server_address, new_host, MAX_IPV4_HOSTLENGTH) && - (config.host_name && !strncmp(config.host_name, new_host, MAX_IPV4_HOSTLENGTH)) && - !strcmp(config.server_url, new_url)) { + if (working_state.serverPort == new_port && + !strncmp(working_state.server_address, new_host, MAX_IPV4_HOSTLENGTH) && + (working_state.host_name && + !strncmp(working_state.host_name, new_host, MAX_IPV4_HOSTLENGTH)) && + !strcmp(working_state.server_url, new_url)) { die(STATE_CRITICAL, _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), - config.use_ssl ? "https" : "http", new_host, new_port, new_url, + working_state.use_ssl ? "https" : "http", new_host, new_port, new_url, (config.display_html ? "" : "")); } /* set new values for redirected request */ if (!(config.followsticky & STICKY_HOST)) { - free(config.server_address); - config.server_address = strndup(new_host, MAX_IPV4_HOSTLENGTH); + free(working_state.server_address); + working_state.server_address = strndup(new_host, MAX_IPV4_HOSTLENGTH); } if (!(config.followsticky & STICKY_PORT)) { - config.server_port = (unsigned short)new_port; + working_state.serverPort = (unsigned short)new_port; } - free(config.host_name); - config.host_name = strndup(new_host, MAX_IPV4_HOSTLENGTH); + free(working_state.host_name); + working_state.host_name = strndup(new_host, MAX_IPV4_HOSTLENGTH); /* reset virtual port */ - config.virtual_port = config.server_port; + working_state.virtualPort = working_state.serverPort; free(new_host); - free(config.server_url); - config.server_url = new_url; + free(working_state.server_url); + working_state.server_url = new_url; uriFreeUriMembersA(&uri); if (verbose) { - printf(_("Redirection to %s://%s:%d%s\n"), config.use_ssl ? "https" : "http", - config.host_name ? config.host_name : config.server_address, config.server_port, - config.server_url); + printf(_("Redirection to %s://%s:%d%s\n"), working_state.use_ssl ? "https" : "http", + working_state.host_name ? working_state.host_name : working_state.server_address, + working_state.serverPort, working_state.server_url); } /* TODO: the hash component MUST be taken from the original URL and @@ -1444,7 +1520,13 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config, int red */ cleanup(); - check_http(config, redir_depth); + + redir_wrapper result = { + .redir_depth = redir_depth, + .working_state = working_state, + .error_code = OK, + }; + return result; } /* check whether a file exists */ @@ -1585,31 +1667,34 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { warning_thresholds = optarg; break; case 'H': /* virtual host */ - result.config.host_name = strdup(optarg); - char *p; + result.config.initial_config.host_name = strdup(optarg); + char *tmp_string; size_t host_name_length; - if (result.config.host_name[0] == '[') { - if ((p = strstr(result.config.host_name, "]:")) != NULL) { /* [IPv6]:port */ - result.config.virtual_port = atoi(p + 2); + if (result.config.initial_config.host_name[0] == '[') { + if ((tmp_string = strstr(result.config.initial_config.host_name, "]:")) != + NULL) { /* [IPv6]:port */ + result.config.initial_config.virtualPort = atoi(tmp_string + 2); /* cut off the port */ - host_name_length = strlen(result.config.host_name) - strlen(p) - 1; - free(result.config.host_name); - result.config.host_name = strndup(optarg, host_name_length); + host_name_length = + strlen(result.config.initial_config.host_name) - strlen(tmp_string) - 1; + free(result.config.initial_config.host_name); + result.config.initial_config.host_name = strndup(optarg, host_name_length); } - } else if ((p = strchr(result.config.host_name, ':')) != NULL && - strchr(++p, ':') == NULL) { /* IPv4:port or host:port */ - result.config.virtual_port = atoi(p); + } else if ((tmp_string = strchr(result.config.initial_config.host_name, ':')) != NULL && + strchr(++tmp_string, ':') == NULL) { /* IPv4:port or host:port */ + result.config.initial_config.virtualPort = atoi(tmp_string); /* cut off the port */ - host_name_length = strlen(result.config.host_name) - strlen(p) - 1; - free(result.config.host_name); - result.config.host_name = strndup(optarg, host_name_length); + host_name_length = + strlen(result.config.initial_config.host_name) - strlen(tmp_string) - 1; + free(result.config.initial_config.host_name); + result.config.initial_config.host_name = strndup(optarg, host_name_length); } break; case 'I': /* internet address */ - result.config.server_address = strdup(optarg); + result.config.initial_config.server_address = strdup(optarg); break; case 'u': /* URL path */ - result.config.server_url = strdup(optarg); + result.config.initial_config.server_url = strdup(optarg); break; case 'p': /* Server port */ if (!is_intnonneg(optarg)) { @@ -1618,7 +1703,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { if (strtol(optarg, NULL, 10) > MAX_PORT) { usage2(_("Invalid port number, supplied port number is too big"), optarg); } - result.config.server_port = (unsigned short)strtol(optarg, NULL, 10); + result.config.initial_config.serverPort = (unsigned short)strtol(optarg, NULL, 10); specify_port = true; } break; @@ -1631,18 +1716,18 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { result.config.proxy_auth[MAX_INPUT_BUFFER - 1] = 0; break; case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */ - if (!result.config.http_post_data) { - result.config.http_post_data = strdup(optarg); + if (!result.config.initial_config.http_post_data) { + result.config.initial_config.http_post_data = strdup(optarg); } - if (!result.config.http_method) { - result.config.http_method = strdup("POST"); + if (!result.config.initial_config.http_method) { + result.config.initial_config.http_method = strdup("POST"); } break; case 'j': /* Set HTTP method */ - if (result.config.http_method) { - free(result.config.http_method); + if (result.config.initial_config.http_method) { + free(result.config.initial_config.http_method); } - result.config.http_method = strdup(optarg); + result.config.initial_config.http_method = strdup(optarg); break; case 'A': /* useragent */ strncpy(result.config.user_agent, optarg, DEFAULT_BUFFER_SIZE); @@ -1723,7 +1808,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { { enable_ssl: bool got_plus = false; - result.config.use_ssl = true; + result.config.initial_config.use_ssl = true; /* ssl_version initialized to CURL_SSLVERSION_DEFAULT as a default. * Only set if it's non-zero. This helps when we include multiple * parameters, like -S and -C combinations */ @@ -1801,7 +1886,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { printf(_("* Set SSL/TLS version to %d\n"), result.config.ssl_version); } if (!specify_port) { - result.config.server_port = HTTPS_PORT; + result.config.initial_config.serverPort = HTTPS_PORT; } } break; #else /* LIBCURL_FEATURE_SSL */ @@ -1935,7 +2020,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { break; } case 'N': /* no-body */ - result.config.no_body = true; + result.config.initial_config.no_body = true; break; case 'M': /* max-age */ { @@ -1996,21 +2081,22 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } } - int c = optind; + int option_counter = optind; - if (result.config.server_address == NULL && c < argc) { - result.config.server_address = strdup(argv[c++]); + if (result.config.initial_config.server_address == NULL && option_counter < argc) { + result.config.initial_config.server_address = strdup(argv[option_counter++]); } - if (result.config.host_name == NULL && c < argc) { - result.config.host_name = strdup(argv[c++]); + if (result.config.initial_config.host_name == NULL && option_counter < argc) { + result.config.initial_config.host_name = strdup(argv[option_counter++]); } - if (result.config.server_address == NULL) { - if (result.config.host_name == NULL) { + if (result.config.initial_config.server_address == NULL) { + if (result.config.initial_config.host_name == NULL) { usage4(_("You must specify a server address or host name")); } else { - result.config.server_address = strdup(result.config.host_name); + result.config.initial_config.server_address = + strdup(result.config.initial_config.host_name); } } @@ -2024,21 +2110,23 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { printf("* Socket timeout set to %ld seconds\n", result.config.socket_timeout); } - if (result.config.http_method == NULL) { - result.config.http_method = strdup("GET"); + if (result.config.initial_config.http_method == NULL) { + result.config.initial_config.http_method = strdup("GET"); } if (result.config.client_cert && !result.config.client_privkey) { usage4(_("If you use a client certificate you must also specify a private key file")); } - if (result.config.virtual_port == 0) { - result.config.virtual_port = result.config.server_port; + if (result.config.initial_config.virtualPort == 0) { + result.config.initial_config.virtualPort = result.config.initial_config.serverPort; } else { - if ((result.config.use_ssl && result.config.server_port == HTTPS_PORT) || - (!result.config.use_ssl && result.config.server_port == HTTP_PORT)) { + if ((result.config.initial_config.use_ssl && + result.config.initial_config.serverPort == HTTPS_PORT) || + (!result.config.initial_config.use_ssl && + result.config.initial_config.serverPort == HTTP_PORT)) { if (!specify_port) { - result.config.server_port = result.config.virtual_port; + result.config.initial_config.serverPort = result.config.initial_config.virtualPort; } } } @@ -2389,12 +2477,12 @@ size_t curlhelp_buffer_write_callback(void *buffer, size_t size, size_t nmemb, v size_t curlhelp_buffer_read_callback(void *buffer, size_t size, size_t nmemb, void *stream) { curlhelp_read_curlbuf *buf = (curlhelp_read_curlbuf *)stream; - size_t n = min(nmemb * size, buf->buflen - buf->pos); + size_t minimalSize = min(nmemb * size, buf->buflen - buf->pos); - memcpy(buffer, buf->buf + buf->pos, n); - buf->pos += n; + memcpy(buffer, buf->buf + buf->pos, minimalSize); + buf->pos += minimalSize; - return n; + return minimalSize; } void curlhelp_freewritebuffer(curlhelp_write_curlbuf *buf) { @@ -2473,86 +2561,85 @@ int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) char *first_line_buf = strdup(status_line->first_line); /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */ - char *p = strtok(first_line_buf, "/"); - if (p == NULL) { + char *temp_string = strtok(first_line_buf, "/"); + if (temp_string == NULL) { free(first_line_buf); return -1; } - if (strcmp(p, "HTTP") != 0) { + if (strcmp(temp_string, "HTTP") != 0) { free(first_line_buf); return -1; } - p = strtok(NULL, " "); - if (p == NULL) { + temp_string = strtok(NULL, " "); + if (temp_string == NULL) { free(first_line_buf); return -1; } - char *pp; - if (strchr(p, '.') != NULL) { + char *temp_string_2; + if (strchr(temp_string, '.') != NULL) { /* HTTP 1.x case */ - strtok(p, "."); - status_line->http_major = (int)strtol(p, &pp, 10); - if (*pp != '\0') { + strtok(temp_string, "."); + status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10); + if (*temp_string_2 != '\0') { free(first_line_buf); return -1; } strtok(NULL, " "); - status_line->http_minor = (int)strtol(p, &pp, 10); - if (*pp != '\0') { + status_line->http_minor = (int)strtol(temp_string, &temp_string_2, 10); + if (*temp_string_2 != '\0') { free(first_line_buf); return -1; } - p += 4; /* 1.x SP */ + temp_string += 4; /* 1.x SP */ } else { /* HTTP 2 case */ - status_line->http_major = (int)strtol(p, &pp, 10); + status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10); status_line->http_minor = 0; - p += 2; /* 2 SP */ + temp_string += 2; /* 2 SP */ } /* status code: "404" or "404.1", then SP */ - - p = strtok(p, " "); - if (p == NULL) { + temp_string = strtok(temp_string, " "); + if (temp_string == NULL) { free(first_line_buf); return -1; } - if (strchr(p, '.') != NULL) { + if (strchr(temp_string, '.') != NULL) { char *ppp; - ppp = strtok(p, "."); - status_line->http_code = (int)strtol(ppp, &pp, 10); - if (*pp != '\0') { + ppp = strtok(temp_string, "."); + status_line->http_code = (int)strtol(ppp, &temp_string_2, 10); + if (*temp_string_2 != '\0') { free(first_line_buf); return -1; } ppp = strtok(NULL, ""); - status_line->http_subcode = (int)strtol(ppp, &pp, 10); - if (*pp != '\0') { + status_line->http_subcode = (int)strtol(ppp, &temp_string_2, 10); + if (*temp_string_2 != '\0') { free(first_line_buf); return -1; } - p += 6; /* 400.1 SP */ + temp_string += 6; /* 400.1 SP */ } else { - status_line->http_code = (int)strtol(p, &pp, 10); + status_line->http_code = (int)strtol(temp_string, &temp_string_2, 10); status_line->http_subcode = -1; - if (*pp != '\0') { + if (*temp_string_2 != '\0') { free(first_line_buf); return -1; } - p += 4; /* 400 SP */ + temp_string += 4; /* 400 SP */ } /* Human readable message: "Not Found" CRLF */ - p = strtok(p, ""); - if (p == NULL) { + temp_string = strtok(temp_string, ""); + if (temp_string == NULL) { status_line->msg = ""; return 0; } - status_line->msg = status_line->first_line + (p - first_line_buf); + status_line->msg = status_line->first_line + (temp_string - first_line_buf); free(first_line_buf); return 0; diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index 97fbcaab..9de68713 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -34,10 +34,37 @@ enum { typedef struct { char *server_address; - unsigned short server_port; - unsigned short virtual_port; - char *host_name; char *server_url; + char *host_name; + + char *http_method; + + char *http_post_data; + + unsigned short virtualPort; + unsigned short serverPort; + + bool use_ssl; + bool no_body; +} check_curl_working_state; + +check_curl_working_state check_curl_working_state_init() { + check_curl_working_state result = { + .server_address = NULL, + .server_url = DEFAULT_SERVER_URL, + .host_name = NULL, + .http_method = NULL, + .http_post_data = NULL, + .virtualPort = 0, + .serverPort = 0, + .use_ssl = false, + .no_body = false, + }; + return result; +} + +typedef struct { + check_curl_working_state initial_config; sa_family_t sin_family; bool automatic_decompression; @@ -45,13 +72,10 @@ typedef struct { char *client_cert; char *client_privkey; char *ca_cert; - bool use_ssl; int ssl_version; - char *http_method; char user_agent[DEFAULT_BUFFER_SIZE]; char **http_opt_headers; size_t http_opt_headers_count; - char *http_post_data; int max_depth; char *http_content_type; long socket_timeout; @@ -59,7 +83,6 @@ typedef struct { char proxy_auth[MAX_INPUT_BUFFER]; int followmethod; int followsticky; - bool no_body; int curl_http_version; char *cookie_jar_file; @@ -88,11 +111,8 @@ typedef struct { check_curl_config check_curl_config_init() { check_curl_config tmp = { - .server_address = NULL, - .server_port = HTTP_PORT, - .virtual_port = 0, - .host_name = NULL, - .server_url = strdup(DEFAULT_SERVER_URL), + .initial_config = check_curl_working_state_init(), + .sin_family = AF_UNSPEC, .automatic_decompression = false, @@ -100,13 +120,10 @@ check_curl_config check_curl_config_init() { .client_cert = NULL, .client_privkey = NULL, .ca_cert = NULL, - .use_ssl = false, .ssl_version = CURL_SSLVERSION_DEFAULT, - .http_method = NULL, .user_agent = {'\0'}, .http_opt_headers = NULL, .http_opt_headers_count = 0, - .http_post_data = NULL, .max_depth = DEFAULT_MAX_REDIRS, .http_content_type = NULL, .socket_timeout = DEFAULT_SOCKET_TIMEOUT, @@ -114,7 +131,6 @@ check_curl_config check_curl_config_init() { .proxy_auth = "", .followmethod = FOLLOW_HTTP_CURL, .followsticky = STICKY_NONE, - .no_body = false, .curl_http_version = CURL_HTTP_VERSION_NONE, .cookie_jar_file = NULL, -- cgit v1.2.3-74-g34f1 From 01f353228424376b25f20d9b105aaf5b3c7a6f65 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 10 Sep 2025 11:07:55 +0200 Subject: check_curl: remove another global variable --- plugins/check_curl.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 76dcbfd8..f28bcdfc 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -145,7 +145,6 @@ check_curl_global_state global_state = { .curl = NULL, }; -static long httpReturnCode; static char errbuf[MAX_INPUT_BUFFER]; static char msg[DEFAULT_BUFFER_SIZE]; typedef union { @@ -257,7 +256,6 @@ int main(int argc, char **argv) { #ifdef HAVE_SSL # ifdef USE_OPENSSL - int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { (void)preverify_ok; /* TODO: we get all certificates of the chain, so which ones @@ -297,7 +295,6 @@ CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) { return CURLE_OK; } - # endif /* USE_OPENSSL */ #endif /* HAVE_SSL */ @@ -1108,11 +1105,12 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat perfstring); /* we cannot know the major/minor version here for sure as we cannot parse the first * line */ - die(STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x %ld unknown - %s", httpReturnCode, msg); + die(STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x unknown - %s", msg); } global_state.status_line_initialized = true; /* get result code from cURL */ + long httpReturnCode; handle_curl_option_return_code( curl_easy_getinfo(global_state.curl, CURLINFO_RESPONSE_CODE, &httpReturnCode), "CURLINFO_RESPONSE_CODE"); -- cgit v1.2.3-74-g34f1 From c06ea4e44caee74486e2931154021a0d0e6c8066 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 10 Sep 2025 11:08:52 +0200 Subject: check_curl: pre compile regex for string matching --- plugins/check_curl.c | 7 ++++--- plugins/check_curl.d/config.h | 8 ++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index f28bcdfc..ba38854a 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -1261,9 +1261,8 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } if (strlen(config.regexp)) { - regex_t preg; regmatch_t pmatch[REGS]; - int errcode = regexec(&preg, global_state.body_buf.buf, REGS, pmatch, 0); + int errcode = regexec(&config.compiled_regex, global_state.body_buf.buf, REGS, pmatch, 0); if ((errcode == 0 && !config.invert_regex) || (errcode == REG_NOMATCH && config.invert_regex)) { /* OK - No-op to avoid changing the logic around it */ @@ -1284,7 +1283,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } result = config.state_regex; } else { - regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); + regerror(errcode, &config.compiled_regex, errbuf, MAX_INPUT_BUFFER); char tmp[DEFAULT_BUFFER_SIZE]; @@ -1969,6 +1968,8 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { result.errorcode = ERROR; return result; } + + result.config.compiled_regex = preg; break; case INVERT_REGEX: result.config.invert_regex = true; diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index 9de68713..9acd4048 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -8,6 +8,7 @@ #include #include #include "curl/curl.h" +#include "regex.h" enum { MAX_RE_SIZE = 1024, @@ -87,7 +88,13 @@ typedef struct { char *cookie_jar_file; int maximum_age; + + // the original regex string from the command line char regexp[MAX_RE_SIZE]; + + // the compiled regex for usage later + regex_t compiled_regex; + mp_state_enum state_regex; bool invert_regex; bool verify_peer_and_host; @@ -136,6 +143,7 @@ check_curl_config check_curl_config_init() { .maximum_age = -1, .regexp = {}, + .compiled_regex = {}, .state_regex = STATE_CRITICAL, .invert_regex = false, .verify_peer_and_host = false, -- cgit v1.2.3-74-g34f1 From 2c81d1257bd2cccb57c2e9f02fbfc42c70323217 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 10 Sep 2025 12:06:48 +0200 Subject: check_curl: remove goto logic --- plugins/check_curl.c | 260 +++++++++++++++++++++++++++------------------------ 1 file changed, 138 insertions(+), 122 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index ba38854a..897ca9e1 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -1628,6 +1628,8 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { char *critical_thresholds = NULL; int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE; bool specify_port = false; + bool enable_tls = false; + char *tls_option_optarg = NULL; while (true) { int option_index = getopt_long( @@ -1748,152 +1750,82 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { result.config.display_html = false; break; case 'C': /* Check SSL cert validity */ -#ifdef LIBCURL_FEATURE_SSL - char *temp; - if ((temp = strchr(optarg, ',')) != NULL) { - *temp = '\0'; - if (!is_intnonneg(optarg)) { - usage2(_("Invalid certificate expiration period"), optarg); - } - result.config.days_till_exp_warn = atoi(optarg); - *temp = ','; - temp++; - if (!is_intnonneg(temp)) { - usage2(_("Invalid certificate expiration period"), temp); - } - result.config.days_till_exp_crit = atoi(temp); - } else { - result.config.days_till_exp_crit = 0; - if (!is_intnonneg(optarg)) { - usage2(_("Invalid certificate expiration period"), optarg); +#ifndef LIBCURL_FEATURE_SSL + usage4(_("Invalid option - SSL is not available")); +#endif + { + char *temp; + if ((temp = strchr(optarg, ',')) != NULL) { + *temp = '\0'; + if (!is_intnonneg(optarg)) { + usage2(_("Invalid certificate expiration period"), optarg); + } + result.config.days_till_exp_warn = atoi(optarg); + *temp = ','; + temp++; + if (!is_intnonneg(temp)) { + usage2(_("Invalid certificate expiration period"), temp); + } + result.config.days_till_exp_crit = atoi(temp); + } else { + result.config.days_till_exp_crit = 0; + if (!is_intnonneg(optarg)) { + usage2(_("Invalid certificate expiration period"), optarg); + } + result.config.days_till_exp_warn = atoi(optarg); } - result.config.days_till_exp_warn = atoi(optarg); + result.config.check_cert = true; + enable_tls = true; } - result.config.check_cert = true; - goto enable_ssl; -#endif + break; case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */ #ifdef HAVE_SSL result.config.continue_after_check_cert = true; break; #endif case 'J': /* use client certificate */ -#ifdef LIBCURL_FEATURE_SSL +#ifndef LIBCURL_FEATURE_SSL + usage4(_("Invalid option - SSL is not available")); +#endif test_file(optarg); result.config.client_cert = optarg; - goto enable_ssl; -#endif + enable_tls = true; + break; case 'K': /* use client private key */ -#ifdef LIBCURL_FEATURE_SSL +#ifndef LIBCURL_FEATURE_SSL + usage4(_("Invalid option - SSL is not available")); +#endif test_file(optarg); result.config.client_privkey = optarg; - goto enable_ssl; -#endif -#ifdef LIBCURL_FEATURE_SSL + enable_tls = true; + break; case CA_CERT_OPTION: /* use CA chain file */ +#ifndef LIBCURL_FEATURE_SSL + usage4(_("Invalid option - SSL is not available")); +#endif test_file(optarg); result.config.ca_cert = optarg; - goto enable_ssl; -#endif -#ifdef LIBCURL_FEATURE_SSL + enable_tls = true; + break; case 'D': /* verify peer certificate & host */ +#ifndef LIBCURL_FEATURE_SSL + usage4(_("Invalid option - SSL is not available")); +#endif result.config.verify_peer_and_host = true; + enable_tls = true; break; -#endif case 'S': /* use SSL */ -#ifdef LIBCURL_FEATURE_SSL - { - enable_ssl: - bool got_plus = false; - result.config.initial_config.use_ssl = true; - /* ssl_version initialized to CURL_SSLVERSION_DEFAULT as a default. - * Only set if it's non-zero. This helps when we include multiple - * parameters, like -S and -C combinations */ - result.config.ssl_version = CURL_SSLVERSION_DEFAULT; - if (option_index == 'S' && optarg != NULL) { - char *plus_ptr = strchr(optarg, '+'); - if (plus_ptr) { - got_plus = true; - *plus_ptr = '\0'; - } - - if (optarg[0] == '2') { - result.config.ssl_version = CURL_SSLVERSION_SSLv2; - } else if (optarg[0] == '3') { - result.config.ssl_version = CURL_SSLVERSION_SSLv3; - } else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0")) { -# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) - result.config.ssl_version = CURL_SSLVERSION_TLSv1_0; -# else - result.config.ssl_version = CURL_SSLVERSION_DEFAULT; -# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ - } else if (!strcmp(optarg, "1.1")) { -# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) - result.config.ssl_version = CURL_SSLVERSION_TLSv1_1; -# else - result.config.ssl_version = CURL_SSLVERSION_DEFAULT; -# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ - } else if (!strcmp(optarg, "1.2")) { -# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) - result.config.ssl_version = CURL_SSLVERSION_TLSv1_2; -# else - result.config.ssl_version = CURL_SSLVERSION_DEFAULT; -# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ - } else if (!strcmp(optarg, "1.3")) { -# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) - result.config.ssl_version = CURL_SSLVERSION_TLSv1_3; -# else - result.config.ssl_version = CURL_SSLVERSION_DEFAULT; -# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */ - } else { - usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 " - "(with optional '+' suffix)")); - } - } -# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) - if (got_plus) { - switch (result.config.ssl_version) { - case CURL_SSLVERSION_TLSv1_3: - result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; - break; - case CURL_SSLVERSION_TLSv1_2: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_0: - result.config.ssl_version |= CURL_SSLVERSION_MAX_DEFAULT; - break; - } - } else { - switch (result.config.ssl_version) { - case CURL_SSLVERSION_TLSv1_3: - result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; - break; - case CURL_SSLVERSION_TLSv1_2: - result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2; - break; - case CURL_SSLVERSION_TLSv1_1: - result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1; - break; - case CURL_SSLVERSION_TLSv1_0: - result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0; - break; - } - } -# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */ - if (verbose >= 2) { - printf(_("* Set SSL/TLS version to %d\n"), result.config.ssl_version); - } - if (!specify_port) { - result.config.initial_config.serverPort = HTTPS_PORT; - } - } break; -#else /* LIBCURL_FEATURE_SSL */ - /* -C -J and -K fall through to here without SSL */ + tls_option_optarg = optarg; + enable_tls = true; +#ifndef LIBCURL_FEATURE_SSL usage4(_("Invalid option - SSL is not available")); +#endif break; case SNI_OPTION: /* --sni is parsed, but ignored, the default is true with libcurl */ - use_sni = true; - break; +#ifndef LIBCURL_FEATURE_SSL + usage4(_("Invalid option - SSL is not available")); #endif /* LIBCURL_FEATURE_SSL */ + break; case MAX_REDIRS_OPTION: if (!is_intnonneg(optarg)) { usage2(_("Invalid max_redirs count"), optarg); @@ -2080,6 +2012,90 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } } + if (enable_tls) { + bool got_plus = false; + result.config.initial_config.use_ssl = true; + /* ssl_version initialized to CURL_SSLVERSION_DEFAULT as a default. + * Only set if it's non-zero. This helps when we include multiple + * parameters, like -S and -C combinations */ + result.config.ssl_version = CURL_SSLVERSION_DEFAULT; + if (tls_option_optarg != NULL) { + char *plus_ptr = strchr(optarg, '+'); + if (plus_ptr) { + got_plus = true; + *plus_ptr = '\0'; + } + + if (optarg[0] == '2') { + result.config.ssl_version = CURL_SSLVERSION_SSLv2; + } else if (optarg[0] == '3') { + result.config.ssl_version = CURL_SSLVERSION_SSLv3; + } else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0")) { +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) + result.config.ssl_version = CURL_SSLVERSION_TLSv1_0; +#else + result.config.ssl_version = CURL_SSLVERSION_DEFAULT; +#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ + } else if (!strcmp(optarg, "1.1")) { +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) + result.config.ssl_version = CURL_SSLVERSION_TLSv1_1; +#else + result.config.ssl_version = CURL_SSLVERSION_DEFAULT; +#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ + } else if (!strcmp(optarg, "1.2")) { +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) + result.config.ssl_version = CURL_SSLVERSION_TLSv1_2; +#else + result.config.ssl_version = CURL_SSLVERSION_DEFAULT; +#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ + } else if (!strcmp(optarg, "1.3")) { +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) + result.config.ssl_version = CURL_SSLVERSION_TLSv1_3; +#else + result.config.ssl_version = CURL_SSLVERSION_DEFAULT; +#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */ + } else { + usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 " + "(with optional '+' suffix)")); + } + } +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) + if (got_plus) { + switch (result.config.ssl_version) { + case CURL_SSLVERSION_TLSv1_3: + result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; + break; + case CURL_SSLVERSION_TLSv1_2: + case CURL_SSLVERSION_TLSv1_1: + case CURL_SSLVERSION_TLSv1_0: + result.config.ssl_version |= CURL_SSLVERSION_MAX_DEFAULT; + break; + } + } else { + switch (result.config.ssl_version) { + case CURL_SSLVERSION_TLSv1_3: + result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; + break; + case CURL_SSLVERSION_TLSv1_2: + result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2; + break; + case CURL_SSLVERSION_TLSv1_1: + result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1; + break; + case CURL_SSLVERSION_TLSv1_0: + result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0; + break; + } + } +#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */ + if (verbose >= 2) { + printf(_("* Set SSL/TLS version to %d\n"), result.config.ssl_version); + } + if (!specify_port) { + result.config.initial_config.serverPort = HTTPS_PORT; + } + } + int option_counter = optind; if (result.config.initial_config.server_address == NULL && option_counter < argc) { -- cgit v1.2.3-74-g34f1 From 4b19852dcc62a69a5a77a45bc9deb0045812104a Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 10 Sep 2025 12:22:46 +0200 Subject: check_curl: remove the other gotos --- plugins/check_curl.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 897ca9e1..7bb67645 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -991,8 +991,13 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat * parsing We only check the first certificate and assume it's the one of * the server */ - const char *raw_cert = NULL; + char *raw_cert = NULL; + bool got_first_cert = false; for (int i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) { + if (got_first_cert) { + break; + } + for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) { if (verbose >= 2) { @@ -1000,11 +1005,12 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } if (strncmp(slist->data, "Cert:", 5) == 0) { raw_cert = &slist->data[5]; - goto GOT_FIRST_CERT; + got_first_cert = true; + break; } } } - GOT_FIRST_CERT: + if (!raw_cert) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Cannot retrieve certificates from CERTINFO information - " @@ -2898,7 +2904,12 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_ printf("**** REQUEST CERTIFICATES ****\n"); } + bool have_first_cert = false; for (int i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) { + if (have_first_cert) { + break; + } + for (slist = cert_ptr->to_certinfo->certinfo[i]; slist; slist = slist->next) { /* find first common name in subject, * TODO: check alternative subjects for @@ -2922,14 +2933,14 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_ } else if (strncasecmp(slist->data, "Expire Date:", 12) == 0) { end_date_str = &slist->data[12]; } else if (strncasecmp(slist->data, "Cert:", 5) == 0) { - goto HAVE_FIRST_CERT; + have_first_cert = true; + break; } if (verbose >= 2) { printf("%d ** %s\n", i, slist->data); } } } -HAVE_FIRST_CERT: if (verbose >= 2) { printf("**** REQUEST CERTIFICATES ****\n"); -- cgit v1.2.3-74-g34f1 From b44cbae7fb6b7bde948c0f26412e9b2a9ef8f78b Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 11 Sep 2025 11:11:44 +0200 Subject: check_curl: less global state --- plugins/check_curl.c | 72 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 31 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 7bb67645..7755d449 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -131,20 +131,6 @@ typedef struct { CURL *curl; } check_curl_global_state; -check_curl_global_state global_state = { - .curl_global_initialized = false, - .curl_easy_initialized = false, - .body_buf_initialized = false, - .body_buf = {}, - .header_buf_initialized = false, - .header_buf = {}, - .status_line_initialized = false, - .status_line = {}, - .put_buf_initialized = false, - .put_buf = {}, - .curl = NULL, -}; - static char errbuf[MAX_INPUT_BUFFER]; static char msg[DEFAULT_BUFFER_SIZE]; typedef union { @@ -166,15 +152,18 @@ static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv* static void handle_curl_option_return_code(CURLcode res, const char *option); static mp_state_enum check_http(check_curl_config /*config*/, check_curl_working_state workingState, - int redir_depth, struct curl_slist *header_list); + int redir_depth, struct curl_slist *header_list, + check_curl_global_state global_state); typedef struct { int redir_depth; check_curl_working_state working_state; int error_code; + check_curl_global_state curl_state; } redir_wrapper; static redir_wrapper redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/, - int redir_depth, check_curl_working_state working_state); + int redir_depth, check_curl_working_state working_state, + check_curl_global_state global_state); static char *perfd_time(double elapsed_time, thresholds * /*thlds*/, long /*socket_timeout*/); static char *perfd_time_connect(double elapsed_time_connect, long /*socket_timeout*/); @@ -246,12 +235,24 @@ int main(int argc, char **argv) { config.initial_config.server_url); } - int redir_depth = 0; + check_curl_global_state global_state = { + .curl_global_initialized = false, + .curl_easy_initialized = false, + .body_buf_initialized = false, + .body_buf = {}, + .header_buf_initialized = false, + .header_buf = {}, + .status_line_initialized = false, + .status_line = {}, + .put_buf_initialized = false, + .put_buf = {}, + .curl = NULL, + }; check_curl_working_state working_state = config.initial_config; struct curl_slist *header_list = NULL; - exit((int)check_http(config, working_state, redir_depth, header_list)); + exit((int)check_http(config, working_state, 0, header_list, global_state)); } #ifdef HAVE_SSL @@ -403,7 +404,7 @@ int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_fam return 0; } -static void cleanup(void) { +static void cleanup(check_curl_global_state global_state) { if (global_state.status_line_initialized) { curlhelp_free_statusline(&global_state.status_line); } @@ -436,7 +437,12 @@ static void cleanup(void) { } mp_state_enum check_http(const check_curl_config config, check_curl_working_state workingState, - int redir_depth, struct curl_slist *header_list) { + int redir_depth, struct curl_slist *header_list, + check_curl_global_state global_state) { + + // ======================= + // Initialisation for curl + // ======================= /* initialize curl */ if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n"); @@ -448,9 +454,6 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } global_state.curl_easy_initialized = true; - /* register cleanup function to shut down libcurl properly */ - atexit(cleanup); - if (verbose >= 1) { handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_VERBOSE, 1), "CURLOPT_VERBOSE"); @@ -651,7 +654,6 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat curl_easy_setopt(global_state.curl, CURLOPT_HTTPHEADER, header_list), "CURLOPT_HTTPHEADER"); #ifdef LIBCURL_FEATURE_SSL - /* set SSL version, warn about insecure or unsupported versions */ if (workingState.use_ssl) { handle_curl_option_return_code( @@ -779,7 +781,6 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat "CURLOPT_SSL_CTX_FUNCTION"); } # endif - #endif /* LIBCURL_FEATURE_SSL */ /* set default or user-given user agent identification */ @@ -936,7 +937,9 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } } - /* do the request */ + // ============== + // do the request + // ============== CURLcode res = curl_easy_perform(global_state.curl); if (verbose >= 2 && workingState.http_post_data) { @@ -960,6 +963,10 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } + // ========== + // Evaluation + // ========== + mp_state_enum result_ssl = STATE_OK; /* certificate checks */ #ifdef LIBCURL_FEATURE_SSL @@ -1173,10 +1180,10 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat * back here, we are in the same status as with * the libcurl method */ - redir_wrapper redir_result = - redir(&global_state.header_buf, config, redir_depth, workingState); + redir_wrapper redir_result = redir(&global_state.header_buf, config, + redir_depth, workingState, global_state); check_http(config, redir_result.working_state, redir_result.redir_depth, - header_list); + header_list, redir_result.curl_state); } } else { /* this is a specific code in the command line to @@ -1195,9 +1202,11 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat handle_curl_option_return_code( curl_easy_getinfo(global_state.curl, CURLINFO_REDIRECT_COUNT, &redir_depth), "CURLINFO_REDIRECT_COUNT"); + if (verbose >= 2) { printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth); } + if (redir_depth > config.max_depth) { snprintf(msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl", config.max_depth); @@ -1365,7 +1374,8 @@ char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) { } redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config config, - int redir_depth, check_curl_working_state working_state) { + int redir_depth, check_curl_working_state working_state, + check_curl_global_state global_state) { curlhelp_statusline status_line; struct phr_header headers[255]; size_t msglen; @@ -1522,7 +1532,7 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config * attached to the URL in Location */ - cleanup(); + cleanup(global_state); redir_wrapper result = { .redir_depth = redir_depth, -- cgit v1.2.3-74-g34f1 From 977e0a7f8bd3700f8514b1409205296d42216ed9 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 11 Sep 2025 11:14:29 +0200 Subject: check_curl: fix function signature --- plugins/check_curl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 7755d449..0784fb8b 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -198,7 +198,8 @@ static size_t get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_write_curlbuf *body_buf); #if defined(HAVE_SSL) && defined(USE_OPENSSL) -int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit); +mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, + int days_till_exp_crit); #endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ static void test_file(char * /*path*/); -- cgit v1.2.3-74-g34f1 From 6969f5719268dec6459d9107e11a711dab3a18dd Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 11 Sep 2025 11:24:16 +0200 Subject: check_curl: improve option handling a bit --- plugins/check_curl.c | 10 +++++----- plugins/check_curl.d/config.h | 13 +++++++++---- 2 files changed, 14 insertions(+), 9 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 0784fb8b..485a8744 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -1139,7 +1139,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } /* make sure the status line matches the response we are looking for */ - if (!expected_statuscode(global_state.status_line.first_line, config.server_expect)) { + if (!expected_statuscode(global_state.status_line.first_line, config.server_expect.string)) { if (workingState.serverPort == HTTP_PORT) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), global_state.status_line.first_line); @@ -1153,7 +1153,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } mp_state_enum result = STATE_OK; - if (config.server_expect_yn) { + if (config.server_expect.is_present) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), config.server_expect); if (verbose) { @@ -1893,9 +1893,9 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { result.config.string_expect[MAX_INPUT_BUFFER - 1] = 0; break; case 'e': /* string or substring */ - strncpy(result.config.server_expect, optarg, MAX_INPUT_BUFFER - 1); - result.config.server_expect[MAX_INPUT_BUFFER - 1] = 0; - result.config.server_expect_yn = true; + strncpy(result.config.server_expect.string, optarg, MAX_INPUT_BUFFER - 1); + result.config.server_expect.string[MAX_INPUT_BUFFER - 1] = 0; + result.config.server_expect.is_present = true; break; case 'T': /* Content-type */ result.config.http_content_type = strdup(optarg); diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index 25cfeda3..7566b19c 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -105,8 +105,10 @@ typedef struct { thresholds *thlds; size_t min_page_len; size_t max_page_len; - char server_expect[MAX_INPUT_BUFFER]; - bool server_expect_yn; + struct { + char string[MAX_INPUT_BUFFER]; + bool is_present; + } server_expect; char string_expect[MAX_INPUT_BUFFER]; char header_expect[MAX_INPUT_BUFFER]; mp_state_enum onredirect; @@ -154,8 +156,11 @@ check_curl_config check_curl_config_init() { .thlds = NULL, .min_page_len = 0, .max_page_len = 0, - .server_expect = HTTP_EXPECT, - .server_expect_yn = false, + .server_expect = + { + .string = HTTP_EXPECT, + .is_present = false, + }, .string_expect = "", .header_expect = "", .onredirect = STATE_OK, -- cgit v1.2.3-74-g34f1 From 99206dab7aa272e5c16c672dca81e6044ac7a4eb Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 11 Sep 2025 13:44:55 +0200 Subject: check_curl: refactoring to modularize code --- plugins/check_curl.c | 1055 +++++++++++++++++++++-------------------- plugins/check_curl.d/config.h | 78 +-- 2 files changed, 594 insertions(+), 539 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 485a8744..f3cf7422 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -129,6 +129,9 @@ typedef struct { bool put_buf_initialized; curlhelp_read_curlbuf put_buf; CURL *curl; + + struct curl_slist *header_list; + struct curl_slist *host; } check_curl_global_state; static char errbuf[MAX_INPUT_BUFFER]; @@ -152,8 +155,7 @@ static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv* static void handle_curl_option_return_code(CURLcode res, const char *option); static mp_state_enum check_http(check_curl_config /*config*/, check_curl_working_state workingState, - int redir_depth, struct curl_slist *header_list, - check_curl_global_state global_state); + int redir_depth); typedef struct { int redir_depth; @@ -162,8 +164,7 @@ typedef struct { check_curl_global_state curl_state; } redir_wrapper; static redir_wrapper redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/, - int redir_depth, check_curl_working_state working_state, - check_curl_global_state global_state); + int redir_depth, check_curl_working_state working_state); static char *perfd_time(double elapsed_time, thresholds * /*thlds*/, long /*socket_timeout*/); static char *perfd_time_connect(double elapsed_time_connect, long /*socket_timeout*/); @@ -172,371 +173,169 @@ static char *perfd_time_firstbyte(double elapsed_time_firstbyte, long /*socket_t static char *perfd_time_headers(double elapsed_time_headers, long /*socket_timeout*/); static char *perfd_time_transfer(double elapsed_time_transfer, long /*socket_timeout*/); static char *perfd_size(size_t page_len, int /*min_page_len*/); + static void print_help(void); void print_usage(void); + static void print_curl_version(void); + static int curlhelp_initwritebuffer(curlhelp_write_curlbuf * /*buf*/); static size_t curlhelp_buffer_write_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, void * /*stream*/); static void curlhelp_freewritebuffer(curlhelp_write_curlbuf * /*buf*/); + static int curlhelp_initreadbuffer(curlhelp_read_curlbuf * /*buf*/, const char * /*data*/, size_t /*datalen*/); static size_t curlhelp_buffer_read_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, void * /*stream*/); static void curlhelp_freereadbuffer(curlhelp_read_curlbuf * /*buf*/); + static curlhelp_ssl_library curlhelp_get_ssl_library(void); static const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library /*ssl_library*/); + int net_noopenssl_check_certificate(cert_ptr_union *, int, int); static int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * /*status_line*/); static void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/); + static char *get_header_value(const struct phr_header *headers, size_t nof_headers, const char *header); static mp_state_enum check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, char (*msg)[DEFAULT_BUFFER_SIZE], int /*maximum_age*/); static size_t get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_write_curlbuf *body_buf); +static int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family); -#if defined(HAVE_SSL) && defined(USE_OPENSSL) -mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, - int days_till_exp_crit); -#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ - -static void test_file(char * /*path*/); - -int main(int argc, char **argv) { - setlocale(LC_ALL, ""); - bindtextdomain(PACKAGE, LOCALEDIR); - textdomain(PACKAGE); - - /* Parse extra opts if any */ - argv = np_extra_opts(&argc, argv, progname); - - /* parse arguments */ - check_curl_config_wrapper tmp_config = process_arguments(argc, argv); - if (tmp_config.errorcode == ERROR) { - usage4(_("Could not parse arguments")); - } - - const check_curl_config config = tmp_config.config; - - /* set defaults */ - if (strlen(config.user_agent) == 0) { - snprintf(config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", - progname, NP_VERSION, VERSION, curl_version()); - } - - if (config.display_html) { - printf("", - config.initial_config.use_ssl ? "https" : "http", - config.initial_config.host_name ? config.initial_config.host_name - : config.initial_config.server_address, - config.initial_config.virtualPort ? config.initial_config.virtualPort - : config.initial_config.serverPort, - config.initial_config.server_url); - } - - check_curl_global_state global_state = { - .curl_global_initialized = false, - .curl_easy_initialized = false, - .body_buf_initialized = false, - .body_buf = {}, - .header_buf_initialized = false, - .header_buf = {}, - .status_line_initialized = false, - .status_line = {}, - .put_buf_initialized = false, - .put_buf = {}, - .curl = NULL, - }; - - check_curl_working_state working_state = config.initial_config; - struct curl_slist *header_list = NULL; +// typedef struct { +// int errorcode; +// } check_curl_evaluation_wrapper; +// check_curl_evaluation_wrapper check_curl_evaluate(check_curl_config config, +// mp_check overall[static 1]) {} - exit((int)check_http(config, working_state, 0, header_list, global_state)); -} +CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm); -#ifdef HAVE_SSL -# ifdef USE_OPENSSL -int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { - (void)preverify_ok; - /* TODO: we get all certificates of the chain, so which ones - * should we test? - * TODO: is the last certificate always the server certificate? - */ - cert = X509_STORE_CTX_get_current_cert(x509_ctx); -# if OPENSSL_VERSION_NUMBER >= 0x10100000L - X509_up_ref(cert); -# endif - if (verbose >= 2) { - puts("* SSL verify callback with certificate:"); - printf("* issuer:\n"); - X509_NAME *issuer = X509_get_issuer_name(cert); - X509_NAME_print_ex_fp(stdout, issuer, 5, XN_FLAG_MULTILINE); - printf("* curl verify_callback:\n* subject:\n"); - X509_NAME *subject = X509_get_subject_name(cert); - X509_NAME_print_ex_fp(stdout, subject, 5, XN_FLAG_MULTILINE); - puts(""); - } - return 1; -} - -CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) { - (void)curl; // ignore unused parameter - (void)parm; // ignore unused parameter - if (add_sslctx_verify_fun) { - SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER, verify_callback); - } - - // workaround for issue: - // OpenSSL SSL_read: error:0A000126:SSL routines::unexpected eof while reading, errno 0 - // see discussion https://github.com/openssl/openssl/discussions/22690 -# ifdef SSL_OP_IGNORE_UNEXPECTED_EOF - SSL_CTX_set_options(sslctx, SSL_OP_IGNORE_UNEXPECTED_EOF); -# endif - - return CURLE_OK; -} -# endif /* USE_OPENSSL */ -#endif /* HAVE_SSL */ - -/* returns a string "HTTP/1.x" or "HTTP/2" */ -static char *string_statuscode(int major, int minor) { - static char buf[10]; - - switch (major) { - case 1: - snprintf(buf, sizeof(buf), "HTTP/%d.%d", major, minor); - break; - case 2: - case 3: - snprintf(buf, sizeof(buf), "HTTP/%d", major); - break; - default: - /* assuming here HTTP/N with N>=4 */ - snprintf(buf, sizeof(buf), "HTTP/%d", major); - break; - } - - return buf; -} - -/* Checks if the server 'reply' is one of the expected 'statuscodes' */ -static bool expected_statuscode(const char *reply, const char *statuscodes) { - char *expected; - - if ((expected = strdup(statuscodes)) == NULL) { - die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); - } - - bool result = false; - for (char *code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) { - if (strstr(reply, code) != NULL) { - result = true; - break; - } - } - - free(expected); - return result; -} - -void handle_curl_option_return_code(CURLcode res, const char *option) { - if (res != CURLE_OK) { - snprintf(msg, DEFAULT_BUFFER_SIZE, - _("Error while setting cURL option '%s': cURL returned %d - %s"), option, res, - curl_easy_strerror(res)); - die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); - } -} - -int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family) { - struct addrinfo hints = { - .ai_family = addr_family, - .ai_socktype = SOCK_STREAM, - .ai_flags = AI_CANONNAME, +typedef struct { + int errorcode; + check_curl_global_state curl_state; +} check_curl_configure_curl_wrapper; +check_curl_configure_curl_wrapper +check_curl_configure_curl(const check_curl_static_curl_config config, + check_curl_working_state working_state, bool check_cert, + bool on_redirect_dependent, int follow_method, int max_depth) { + check_curl_configure_curl_wrapper result = { + .errorcode = OK, + .curl_state = + { + .curl_global_initialized = false, + .curl_easy_initialized = false, + .curl = NULL, + + .body_buf_initialized = false, + .body_buf = {}, + .header_buf_initialized = false, + .header_buf = {}, + .status_line_initialized = false, + .status_line = {}, + .put_buf_initialized = false, + .put_buf = {}, + + .header_list = NULL, + .host = NULL, + }, }; - struct addrinfo *result; - int errcode = getaddrinfo(host, NULL, &hints, &result); - if (errcode != 0) { - return errcode; - } - - strcpy(buf, ""); - struct addrinfo *res = result; - - size_t buflen_remaining = buflen - 1; - size_t addrstr_len; - char addrstr[100]; - void *ptr = {0}; - while (res) { - switch (res->ai_family) { - case AF_INET: - ptr = &((struct sockaddr_in *)res->ai_addr)->sin_addr; - break; - case AF_INET6: - ptr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; - break; - } - - inet_ntop(res->ai_family, ptr, addrstr, 100); - if (verbose >= 1) { - printf("* getaddrinfo IPv%d address: %s\n", res->ai_family == PF_INET6 ? 6 : 4, - addrstr); - } - - // Append all IPs to buf as a comma-separated string - addrstr_len = strlen(addrstr); - if (buflen_remaining > addrstr_len + 1) { - if (buf[0] != '\0') { - strncat(buf, ",", buflen_remaining); - buflen_remaining -= 1; - } - strncat(buf, addrstr, buflen_remaining); - buflen_remaining -= addrstr_len; - } - - res = res->ai_next; - } - - freeaddrinfo(result); - - return 0; -} - -static void cleanup(check_curl_global_state global_state) { - if (global_state.status_line_initialized) { - curlhelp_free_statusline(&global_state.status_line); - } - global_state.status_line_initialized = false; - - if (global_state.curl_easy_initialized) { - curl_easy_cleanup(global_state.curl); - } - global_state.curl_easy_initialized = false; - - if (global_state.curl_global_initialized) { - curl_global_cleanup(); - } - global_state.curl_global_initialized = false; - - if (global_state.body_buf_initialized) { - curlhelp_freewritebuffer(&global_state.body_buf); - } - global_state.body_buf_initialized = false; - - if (global_state.header_buf_initialized) { - curlhelp_freewritebuffer(&global_state.header_buf); - } - global_state.header_buf_initialized = false; - - if (global_state.put_buf_initialized) { - curlhelp_freereadbuffer(&global_state.put_buf); - } - global_state.put_buf_initialized = false; -} - -mp_state_enum check_http(const check_curl_config config, check_curl_working_state workingState, - int redir_depth, struct curl_slist *header_list, - check_curl_global_state global_state) { - - // ======================= - // Initialisation for curl - // ======================= - /* initialize curl */ if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n"); } - global_state.curl_global_initialized = true; + result.curl_state.curl_global_initialized = true; - if ((global_state.curl = curl_easy_init()) == NULL) { + if ((result.curl_state.curl = curl_easy_init()) == NULL) { die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n"); } - global_state.curl_easy_initialized = true; + result.curl_state.curl_easy_initialized = true; if (verbose >= 1) { - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_VERBOSE, 1), + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_VERBOSE, 1), "CURLOPT_VERBOSE"); } - /* print everything on stdout like check_http would do */ - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_STDERR, stdout), + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_STDERR, stdout), "CURLOPT_STDERR"); if (config.automatic_decompression) { #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_ACCEPT_ENCODING, ""), + curl_easy_setopt(result.curl_state.curl, CURLOPT_ACCEPT_ENCODING, ""), "CURLOPT_ACCEPT_ENCODING"); #else - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_ENCODING, ""), - "CURLOPT_ENCODING"); + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING"); #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */ } /* initialize buffer for body of the answer */ - if (curlhelp_initwritebuffer(&global_state.body_buf) < 0) { + if (curlhelp_initwritebuffer(&result.curl_state.body_buf) < 0) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n"); } - global_state.body_buf_initialized = true; - handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_WRITEFUNCTION, curlhelp_buffer_write_callback), - "CURLOPT_WRITEFUNCTION"); - handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_WRITEDATA, (void *)&global_state.body_buf), - "CURLOPT_WRITEDATA"); + result.curl_state.body_buf_initialized = true; + + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEFUNCTION, + curlhelp_buffer_write_callback), + "CURLOPT_WRITEFUNCTION"); + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEDATA, + (void *)&result.curl_state.body_buf), + "CURLOPT_WRITEDATA"); /* initialize buffer for header of the answer */ - if (curlhelp_initwritebuffer(&global_state.header_buf) < 0) { + if (curlhelp_initwritebuffer(&result.curl_state.header_buf) < 0) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n"); } - global_state.header_buf_initialized = true; + result.curl_state.header_buf_initialized = true; - handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_HEADERFUNCTION, curlhelp_buffer_write_callback), - "CURLOPT_HEADERFUNCTION"); - handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_WRITEHEADER, (void *)&global_state.header_buf), - "CURLOPT_WRITEHEADER"); + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_HEADERFUNCTION, + curlhelp_buffer_write_callback), + "CURLOPT_HEADERFUNCTION"); + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEHEADER, + (void *)&result.curl_state.header_buf), + "CURLOPT_WRITEHEADER"); /* set the error buffer */ - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_ERRORBUFFER, errbuf), - "CURLOPT_ERRORBUFFER"); + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_ERRORBUFFER, errbuf), + "CURLOPT_ERRORBUFFER"); /* set timeouts */ handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_CONNECTTIMEOUT, config.socket_timeout), + curl_easy_setopt(result.curl_state.curl, CURLOPT_CONNECTTIMEOUT, config.socket_timeout), "CURLOPT_CONNECTTIMEOUT"); handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_TIMEOUT, config.socket_timeout), + curl_easy_setopt(result.curl_state.curl, CURLOPT_TIMEOUT, config.socket_timeout), "CURLOPT_TIMEOUT"); /* enable haproxy protocol */ if (config.haproxy_protocol) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_HAPROXYPROTOCOL, 1L), + curl_easy_setopt(result.curl_state.curl, CURLOPT_HAPROXYPROTOCOL, 1L), "CURLOPT_HAPROXYPROTOCOL"); } // fill dns resolve cache to make curl connect to the given server_address instead of the // host_name, only required for ssl, because we use the host_name later on to make SNI happy - struct curl_slist *host = NULL; char dnscache[DEFAULT_BUFFER_SIZE]; char addrstr[DEFAULT_BUFFER_SIZE / 2]; - if (workingState.use_ssl && workingState.host_name != NULL) { + if (working_state.use_ssl && working_state.host_name != NULL) { int res; - if ((res = lookup_host(workingState.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2, + if ((res = lookup_host(working_state.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2, config.sin_family)) != 0) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), - workingState.server_address, res, gai_strerror(res)); + working_state.server_address, res, gai_strerror(res)); die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } - snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", workingState.host_name, - workingState.serverPort, addrstr); - host = curl_slist_append(NULL, dnscache); - curl_easy_setopt(global_state.curl, CURLOPT_RESOLVE, host); + snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", working_state.host_name, + working_state.serverPort, addrstr); + result.curl_state.host = curl_slist_append(NULL, dnscache); + curl_easy_setopt(result.curl_state.curl, CURLOPT_RESOLVE, result.curl_state.host); if (verbose >= 1) { printf("* curl CURLOPT_RESOLVE: %s\n", dnscache); } @@ -544,72 +343,71 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat // If server_address is an IPv6 address it must be surround by square brackets struct in6_addr tmp_in_addr; - if (inet_pton(AF_INET6, workingState.server_address, &tmp_in_addr) == 1) { - char *new_server_address = malloc(strlen(workingState.server_address) + 3); + if (inet_pton(AF_INET6, working_state.server_address, &tmp_in_addr) == 1) { + char *new_server_address = malloc(strlen(working_state.server_address) + 3); if (new_server_address == NULL) { die(STATE_UNKNOWN, "HTTP UNKNOWN - Unable to allocate memory\n"); } - snprintf(new_server_address, strlen(workingState.server_address) + 3, "[%s]", - workingState.server_address); - free(workingState.server_address); - workingState.server_address = new_server_address; + snprintf(new_server_address, strlen(working_state.server_address) + 3, "[%s]", + working_state.server_address); + working_state.server_address = new_server_address; } /* compose URL: use the address we want to connect to, set Host: header later */ char url[DEFAULT_BUFFER_SIZE]; - snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", workingState.use_ssl ? "https" : "http", - (workingState.use_ssl & (workingState.host_name != NULL)) - ? workingState.host_name - : workingState.server_address, - workingState.serverPort, workingState.server_url); + snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", working_state.use_ssl ? "https" : "http", + (working_state.use_ssl & (working_state.host_name != NULL)) + ? working_state.host_name + : working_state.server_address, + working_state.serverPort, working_state.server_url); if (verbose >= 1) { printf("* curl CURLOPT_URL: %s\n", url); } - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_URL, url), + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, url), "CURLOPT_URL"); /* extract proxy information for legacy proxy https requests */ - if (!strcmp(workingState.http_method, "CONNECT") || - strstr(workingState.server_url, "http") == workingState.server_url) { + if (!strcmp(working_state.http_method, "CONNECT") || + strstr(working_state.server_url, "http") == working_state.server_url) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_PROXY, workingState.server_address), + curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXY, working_state.server_address), "CURLOPT_PROXY"); - handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_PROXYPORT, (long)workingState.serverPort), - "CURLOPT_PROXYPORT"); + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXYPORT, + (long)working_state.serverPort), + "CURLOPT_PROXYPORT"); if (verbose >= 2) { - printf("* curl CURLOPT_PROXY: %s:%d\n", workingState.server_address, - workingState.serverPort); + printf("* curl CURLOPT_PROXY: %s:%d\n", working_state.server_address, + working_state.serverPort); } - workingState.http_method = "GET"; + working_state.http_method = "GET"; handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_URL, workingState.server_url), + curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, working_state.server_url), "CURLOPT_URL"); } /* disable body for HEAD request */ - if (workingState.http_method && !strcmp(workingState.http_method, "HEAD")) { - workingState.no_body = true; + if (working_state.http_method && !strcmp(working_state.http_method, "HEAD")) { + working_state.no_body = true; } /* set HTTP protocol version */ handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_HTTP_VERSION, config.curl_http_version), + curl_easy_setopt(result.curl_state.curl, CURLOPT_HTTP_VERSION, config.curl_http_version), "CURLOPT_HTTP_VERSION"); /* set HTTP method */ - if (workingState.http_method) { - if (!strcmp(workingState.http_method, "POST")) { - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_POST, 1), - "CURLOPT_POST"); - } else if (!strcmp(workingState.http_method, "PUT")) { - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_UPLOAD, 1), - "CURLOPT_UPLOAD"); + if (working_state.http_method) { + if (!strcmp(working_state.http_method, "POST")) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_POST, 1), "CURLOPT_POST"); + } else if (!strcmp(working_state.http_method, "PUT")) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD"); } else { - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_CUSTOMREQUEST, - workingState.http_method), + working_state.http_method), "CURLOPT_CUSTOMREQUEST"); } } @@ -627,67 +425,71 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in * anyway */ char http_header[DEFAULT_BUFFER_SIZE]; - if (workingState.host_name != NULL && force_host_header == NULL) { - if ((workingState.virtualPort != HTTP_PORT && !workingState.use_ssl) || - (workingState.virtualPort != HTTPS_PORT && workingState.use_ssl)) { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", workingState.host_name, - workingState.virtualPort); + if (working_state.host_name != NULL && force_host_header == NULL) { + if ((working_state.virtualPort != HTTP_PORT && !working_state.use_ssl) || + (working_state.virtualPort != HTTPS_PORT && working_state.use_ssl)) { + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", working_state.host_name, + working_state.virtualPort); } else { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", workingState.host_name); + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", working_state.host_name); } - header_list = curl_slist_append(header_list, http_header); + result.curl_state.header_list = + curl_slist_append(result.curl_state.header_list, http_header); } /* always close connection, be nice to servers */ snprintf(http_header, DEFAULT_BUFFER_SIZE, "Connection: close"); - header_list = curl_slist_append(header_list, http_header); + result.curl_state.header_list = curl_slist_append(result.curl_state.header_list, http_header); /* attach additional headers supplied by the user */ /* optionally send any other header tag */ if (config.http_opt_headers_count) { for (size_t i = 0; i < config.http_opt_headers_count; i++) { - header_list = curl_slist_append(header_list, config.http_opt_headers[i]); + result.curl_state.header_list = + curl_slist_append(result.curl_state.header_list, config.http_opt_headers[i]); } } /* set HTTP headers */ handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_HTTPHEADER, header_list), "CURLOPT_HTTPHEADER"); + curl_easy_setopt(result.curl_state.curl, CURLOPT_HTTPHEADER, result.curl_state.header_list), + "CURLOPT_HTTPHEADER"); #ifdef LIBCURL_FEATURE_SSL /* set SSL version, warn about insecure or unsupported versions */ - if (workingState.use_ssl) { + if (working_state.use_ssl) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_SSLVERSION, config.ssl_version), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLVERSION, config.ssl_version), "CURLOPT_SSLVERSION"); } /* client certificate and key to present to server (SSL) */ if (config.client_cert) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_SSLCERT, config.client_cert), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLCERT, config.client_cert), "CURLOPT_SSLCERT"); } if (config.client_privkey) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_SSLKEY, config.client_privkey), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLKEY, config.client_privkey), "CURLOPT_SSLKEY"); } if (config.ca_cert) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_CAINFO, config.ca_cert), "CURLOPT_CAINFO"); + curl_easy_setopt(result.curl_state.curl, CURLOPT_CAINFO, config.ca_cert), + "CURLOPT_CAINFO"); } if (config.ca_cert || config.verify_peer_and_host) { /* per default if we have a CA verify both the peer and the * hostname in the certificate, can be switched off later */ handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_SSL_VERIFYPEER, 1), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 1), "CURLOPT_SSL_VERIFYPEER"); handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_SSL_VERIFYHOST, 2), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 2), "CURLOPT_SSL_VERIFYHOST"); } else { /* backward-compatible behaviour, be tolerant in checks @@ -695,10 +497,10 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat * to be less tolerant about ssl verfications */ handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_SSL_VERIFYPEER, 0), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 0), "CURLOPT_SSL_VERIFYPEER"); handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_SSL_VERIFYHOST, 0), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 0), "CURLOPT_SSL_VERIFYHOST"); } @@ -706,7 +508,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat curlhelp_ssl_library ssl_library = curlhelp_get_ssl_library(); /* try hard to get a stack of certificates to verify against */ - if (config.check_cert) { + if (check_cert) { # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) /* inform curl to report back certificates */ switch (ssl_library) { @@ -722,14 +524,14 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat /* libcurl is built with OpenSSL, monitoring plugins, so falling * back to manually extracting certificate information */ handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); + curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); break; case CURLHELP_SSL_LIBRARY_NSS: # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) /* NSS: support for CERTINFO is implemented since 7.34.0 */ handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); + curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); # else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library " @@ -742,7 +544,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */ handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); + curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); # else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */ die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library " @@ -775,10 +577,10 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat # if LIBCURL_VERSION_NUM >= \ MAKE_LIBCURL_VERSION(7, 10, 6) /* required for CURLOPT_SSL_CTX_FUNCTION */ // ssl ctx function is not available with all ssl backends - if (curl_easy_setopt(global_state.curl, CURLOPT_SSL_CTX_FUNCTION, NULL) != + if (curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_CTX_FUNCTION, NULL) != CURLE_UNKNOWN_OPTION) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION"); } # endif @@ -786,23 +588,22 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat /* set default or user-given user agent identification */ handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_USERAGENT, config.user_agent), + curl_easy_setopt(result.curl_state.curl, CURLOPT_USERAGENT, config.user_agent), "CURLOPT_USERAGENT"); /* proxy-authentication */ if (strcmp(config.proxy_auth, "")) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_PROXYUSERPWD, config.proxy_auth), + curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXYUSERPWD, config.proxy_auth), "CURLOPT_PROXYUSERPWD"); } /* authentication */ if (strcmp(config.user_auth, "")) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_USERPWD, config.user_auth), + curl_easy_setopt(result.curl_state.curl, CURLOPT_USERPWD, config.user_auth), "CURLOPT_USERPWD"); } - /* TODO: parameter auth method, bitfield of following methods: * CURLAUTH_BASIC (default) * CURLAUTH_DIGEST @@ -820,26 +621,26 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat */ /* handle redirections */ - if (config.onredirect == STATE_DEPENDENT) { - if (config.followmethod == FOLLOW_LIBCURL) { + if (on_redirect_dependent) { + if (follow_method == FOLLOW_LIBCURL) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_FOLLOWLOCATION, 1), + curl_easy_setopt(result.curl_state.curl, CURLOPT_FOLLOWLOCATION, 1), "CURLOPT_FOLLOWLOCATION"); /* default -1 is infinite, not good, could lead to zombie plugins! Setting it to one bigger than maximal limit to handle errors nicely below */ handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_MAXREDIRS, config.max_depth + 1), + curl_easy_setopt(result.curl_state.curl, CURLOPT_MAXREDIRS, max_depth + 1), "CURLOPT_MAXREDIRS"); /* for now allow only http and https (we are a http(s) check plugin in the end) */ #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 85, 0) handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"), + curl_easy_setopt(result.curl_state.curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"), "CURLOPT_REDIR_PROTOCOLS_STR"); #elif LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4) - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS), "CURLOPT_REDIRECT_PROTOCOLS"); @@ -854,70 +655,72 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat option here is nice like for expected page size? */ } else { - /* old style redirection is handled below */ + /* old style redirection*/ } } - /* no-body */ - if (workingState.no_body) { - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_NOBODY, 1), + if (working_state.no_body) { + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_NOBODY, 1), "CURLOPT_NOBODY"); } /* IPv4 or IPv6 forced DNS resolution */ if (config.sin_family == AF_UNSPEC) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), + curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)"); } else if (config.sin_family == AF_INET) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), + curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)"); } #if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) else if (config.sin_family == AF_INET6) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), + curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)"); } #endif /* either send http POST data (any data, not only POST)*/ - if (!strcmp(workingState.http_method, "POST") || !strcmp(workingState.http_method, "PUT")) { + if (!strcmp(working_state.http_method, "POST") || !strcmp(working_state.http_method, "PUT")) { /* set content of payload for POST and PUT */ if (config.http_content_type) { snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", config.http_content_type); - header_list = curl_slist_append(header_list, http_header); + result.curl_state.header_list = + curl_slist_append(result.curl_state.header_list, http_header); } /* NULL indicates "HTTP Continue" in libcurl, provide an empty string * in case of no POST/PUT data */ - if (!workingState.http_post_data) { - workingState.http_post_data = ""; + if (!working_state.http_post_data) { + working_state.http_post_data = ""; } - if (!strcmp(workingState.http_method, "POST")) { + + if (!strcmp(working_state.http_method, "POST")) { /* POST method, set payload with CURLOPT_POSTFIELDS */ - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_POSTFIELDS, - workingState.http_post_data), + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, + CURLOPT_POSTFIELDS, + working_state.http_post_data), "CURLOPT_POSTFIELDS"); - } else if (!strcmp(workingState.http_method, "PUT")) { + } else if (!strcmp(working_state.http_method, "PUT")) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_READFUNCTION, + curl_easy_setopt(result.curl_state.curl, CURLOPT_READFUNCTION, (curl_read_callback)curlhelp_buffer_read_callback), "CURLOPT_READFUNCTION"); - if (curlhelp_initreadbuffer(&global_state.put_buf, workingState.http_post_data, - strlen(workingState.http_post_data)) < 0) { + if (curlhelp_initreadbuffer(&result.curl_state.put_buf, working_state.http_post_data, + strlen(working_state.http_post_data)) < 0) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating read buffer for PUT\n"); } - global_state.put_buf_initialized = true; - - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_READDATA, - (void *)&global_state.put_buf), + result.curl_state.put_buf_initialized = true; + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, + CURLOPT_READDATA, + (void *)&result.curl_state.put_buf), "CURLOPT_READDATA"); handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_INFILESIZE, - (curl_off_t)strlen(workingState.http_post_data)), + curl_easy_setopt(result.curl_state.curl, CURLOPT_INFILESIZE, + (curl_off_t)strlen(working_state.http_post_data)), "CURLOPT_INFILESIZE"); } } @@ -927,35 +730,267 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat /* enable reading cookies from a file, and if the filename is an empty string, only * enable the curl cookie engine */ handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_COOKIEFILE, config.cookie_jar_file), + curl_easy_setopt(result.curl_state.curl, CURLOPT_COOKIEFILE, config.cookie_jar_file), "CURLOPT_COOKIEFILE"); /* now enable saving cookies to a file, but only if the filename is not an empty string, * since writing it would fail */ if (*config.cookie_jar_file) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_COOKIEJAR, config.cookie_jar_file), + curl_easy_setopt(result.curl_state.curl, CURLOPT_COOKIEJAR, config.cookie_jar_file), "CURLOPT_COOKIEJAR"); } } + return result; +} + +#if defined(HAVE_SSL) && defined(USE_OPENSSL) +mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, + int days_till_exp_crit); +#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ + +static void test_file(char * /*path*/); + +int main(int argc, char **argv) { + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + /* Parse extra opts if any */ + argv = np_extra_opts(&argc, argv, progname); + + /* parse arguments */ + check_curl_config_wrapper tmp_config = process_arguments(argc, argv); + if (tmp_config.errorcode == ERROR) { + usage4(_("Could not parse arguments")); + } + + const check_curl_config config = tmp_config.config; + + if (config.display_html) { + printf("", + config.initial_config.use_ssl ? "https" : "http", + config.initial_config.host_name ? config.initial_config.host_name + : config.initial_config.server_address, + config.initial_config.virtualPort ? config.initial_config.virtualPort + : config.initial_config.serverPort, + config.initial_config.server_url); + } + + check_curl_working_state working_state = config.initial_config; + + exit((int)check_http(config, working_state, 0)); +} + +#ifdef HAVE_SSL +# ifdef USE_OPENSSL +int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { + (void)preverify_ok; + /* TODO: we get all certificates of the chain, so which ones + * should we test? + * TODO: is the last certificate always the server certificate? + */ + cert = X509_STORE_CTX_get_current_cert(x509_ctx); +# if OPENSSL_VERSION_NUMBER >= 0x10100000L + X509_up_ref(cert); +# endif + if (verbose >= 2) { + puts("* SSL verify callback with certificate:"); + printf("* issuer:\n"); + X509_NAME *issuer = X509_get_issuer_name(cert); + X509_NAME_print_ex_fp(stdout, issuer, 5, XN_FLAG_MULTILINE); + printf("* curl verify_callback:\n* subject:\n"); + X509_NAME *subject = X509_get_subject_name(cert); + X509_NAME_print_ex_fp(stdout, subject, 5, XN_FLAG_MULTILINE); + puts(""); + } + return 1; +} + +CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) { + (void)curl; // ignore unused parameter + (void)parm; // ignore unused parameter + if (add_sslctx_verify_fun) { + SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER, verify_callback); + } + + // workaround for issue: + // OpenSSL SSL_read: error:0A000126:SSL routines::unexpected eof while reading, errno 0 + // see discussion https://github.com/openssl/openssl/discussions/22690 +# ifdef SSL_OP_IGNORE_UNEXPECTED_EOF + SSL_CTX_set_options(sslctx, SSL_OP_IGNORE_UNEXPECTED_EOF); +# endif + + return CURLE_OK; +} +# endif /* USE_OPENSSL */ +#endif /* HAVE_SSL */ + +/* returns a string "HTTP/1.x" or "HTTP/2" */ +static char *string_statuscode(int major, int minor) { + static char buf[10]; + + switch (major) { + case 1: + snprintf(buf, sizeof(buf), "HTTP/%d.%d", major, minor); + break; + case 2: + case 3: + snprintf(buf, sizeof(buf), "HTTP/%d", major); + break; + default: + /* assuming here HTTP/N with N>=4 */ + snprintf(buf, sizeof(buf), "HTTP/%d", major); + break; + } + + return buf; +} + +/* Checks if the server 'reply' is one of the expected 'statuscodes' */ +static bool expected_statuscode(const char *reply, const char *statuscodes) { + char *expected; + + if ((expected = strdup(statuscodes)) == NULL) { + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); + } + + bool result = false; + for (char *code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) { + if (strstr(reply, code) != NULL) { + result = true; + break; + } + } + + free(expected); + return result; +} + +void handle_curl_option_return_code(CURLcode res, const char *option) { + if (res != CURLE_OK) { + snprintf(msg, DEFAULT_BUFFER_SIZE, + _("Error while setting cURL option '%s': cURL returned %d - %s"), option, res, + curl_easy_strerror(res)); + die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); + } +} + +int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family) { + struct addrinfo hints = { + .ai_family = addr_family, + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_CANONNAME, + }; + + struct addrinfo *result; + int errcode = getaddrinfo(host, NULL, &hints, &result); + if (errcode != 0) { + return errcode; + } + + strcpy(buf, ""); + struct addrinfo *res = result; + + size_t buflen_remaining = buflen - 1; + size_t addrstr_len; + char addrstr[100]; + void *ptr = {0}; + while (res) { + switch (res->ai_family) { + case AF_INET: + ptr = &((struct sockaddr_in *)res->ai_addr)->sin_addr; + break; + case AF_INET6: + ptr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; + break; + } + + inet_ntop(res->ai_family, ptr, addrstr, 100); + if (verbose >= 1) { + printf("* getaddrinfo IPv%d address: %s\n", res->ai_family == PF_INET6 ? 6 : 4, + addrstr); + } + + // Append all IPs to buf as a comma-separated string + addrstr_len = strlen(addrstr); + if (buflen_remaining > addrstr_len + 1) { + if (buf[0] != '\0') { + strncat(buf, ",", buflen_remaining); + buflen_remaining -= 1; + } + strncat(buf, addrstr, buflen_remaining); + buflen_remaining -= addrstr_len; + } + + res = res->ai_next; + } + + freeaddrinfo(result); + + return 0; +} + +static void cleanup(check_curl_global_state global_state) { + if (global_state.status_line_initialized) { + curlhelp_free_statusline(&global_state.status_line); + } + global_state.status_line_initialized = false; + + if (global_state.curl_easy_initialized) { + curl_easy_cleanup(global_state.curl); + } + global_state.curl_easy_initialized = false; + + if (global_state.curl_global_initialized) { + curl_global_cleanup(); + } + global_state.curl_global_initialized = false; + + if (global_state.body_buf_initialized) { + curlhelp_freewritebuffer(&global_state.body_buf); + } + global_state.body_buf_initialized = false; + + if (global_state.header_buf_initialized) { + curlhelp_freewritebuffer(&global_state.header_buf); + } + global_state.header_buf_initialized = false; + + if (global_state.put_buf_initialized) { + curlhelp_freereadbuffer(&global_state.put_buf); + } + global_state.put_buf_initialized = false; + + if (global_state.header_list) { + curl_slist_free_all(global_state.header_list); + } + + if (global_state.host) { + curl_slist_free_all(global_state.host); + } +} + +mp_state_enum check_http(const check_curl_config config, check_curl_working_state workingState, + int redir_depth) { + + // ======================= + // Initialisation for curl + // ======================= + check_curl_configure_curl_wrapper conf_curl_struct = check_curl_configure_curl( + config.curl_config, workingState, config.check_cert, config.on_redirect_dependent, + config.followmethod, config.max_depth); + + check_curl_global_state curl_state = conf_curl_struct.curl_state; // ============== // do the request // ============== - CURLcode res = curl_easy_perform(global_state.curl); + CURLcode res = curl_easy_perform(curl_state.curl); if (verbose >= 2 && workingState.http_post_data) { printf("**** REQUEST CONTENT ****\n%s\n", workingState.http_post_data); } - /* free header and server IP resolve lists, we don't need it anymore */ - curl_slist_free_all(header_list); - header_list = NULL; - - if (host) { - curl_slist_free_all(host); - host = NULL; - } - /* Curl errors, result in critical Nagios state */ if (res != CURLE_OK) { snprintf(msg, DEFAULT_BUFFER_SIZE, @@ -969,7 +1004,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat // ========== mp_state_enum result_ssl = STATE_OK; - /* certificate checks */ +/* certificate checks */ #ifdef LIBCURL_FEATURE_SSL if (workingState.use_ssl) { if (config.check_cert) { @@ -992,7 +1027,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat cert_ptr_union cert_ptr = {0}; cert_ptr.to_info = NULL; - res = curl_easy_getinfo(global_state.curl, CURLINFO_CERTINFO, &cert_ptr.to_info); + res = curl_easy_getinfo(curl_state.curl, CURLINFO_CERTINFO, &cert_ptr.to_info); if (!res && cert_ptr.to_info) { # ifdef USE_OPENSSL /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert @@ -1066,54 +1101,55 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat */ double total_time; handle_curl_option_return_code( - curl_easy_getinfo(global_state.curl, CURLINFO_TOTAL_TIME, &total_time), + curl_easy_getinfo(curl_state.curl, CURLINFO_TOTAL_TIME, &total_time), "CURLINFO_TOTAL_TIME"); - size_t page_len = get_content_length(&global_state.header_buf, &global_state.body_buf); + size_t page_len = get_content_length(&curl_state.header_buf, &curl_state.body_buf); char perfstring[DEFAULT_BUFFER_SIZE]; if (config.show_extended_perfdata) { double time_connect; handle_curl_option_return_code( - curl_easy_getinfo(global_state.curl, CURLINFO_CONNECT_TIME, &time_connect), + curl_easy_getinfo(curl_state.curl, CURLINFO_CONNECT_TIME, &time_connect), "CURLINFO_CONNECT_TIME"); double time_appconnect; handle_curl_option_return_code( - curl_easy_getinfo(global_state.curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), + curl_easy_getinfo(curl_state.curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), "CURLINFO_APPCONNECT_TIME"); double time_headers; handle_curl_option_return_code( - curl_easy_getinfo(global_state.curl, CURLINFO_PRETRANSFER_TIME, &time_headers), + curl_easy_getinfo(curl_state.curl, CURLINFO_PRETRANSFER_TIME, &time_headers), "CURLINFO_PRETRANSFER_TIME"); double time_firstbyte; handle_curl_option_return_code( - curl_easy_getinfo(global_state.curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), + curl_easy_getinfo(curl_state.curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), "CURLINFO_STARTTRANSFER_TIME"); - snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s", - perfd_time(total_time, config.thlds, config.socket_timeout), - perfd_size(page_len, config.min_page_len), - perfd_time_connect(time_connect, config.socket_timeout), - workingState.use_ssl - ? perfd_time_ssl(time_appconnect - time_connect, config.socket_timeout) - : "", - perfd_time_headers(time_headers - time_appconnect, config.socket_timeout), - perfd_time_firstbyte(time_firstbyte - time_headers, config.socket_timeout), - perfd_time_transfer(total_time - time_firstbyte, config.socket_timeout)); + snprintf( + perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s", + perfd_time(total_time, config.thlds, config.curl_config.socket_timeout), + perfd_size(page_len, config.min_page_len), + perfd_time_connect(time_connect, config.curl_config.socket_timeout), + workingState.use_ssl + ? perfd_time_ssl(time_appconnect - time_connect, config.curl_config.socket_timeout) + : "", + perfd_time_headers(time_headers - time_appconnect, config.curl_config.socket_timeout), + perfd_time_firstbyte(time_firstbyte - time_headers, config.curl_config.socket_timeout), + perfd_time_transfer(total_time - time_firstbyte, config.curl_config.socket_timeout)); } else { snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s", - perfd_time(total_time, config.thlds, config.socket_timeout), + perfd_time(total_time, config.thlds, config.curl_config.socket_timeout), perfd_size(page_len, config.min_page_len)); } /* return a CRITICAL status if we couldn't read any data */ - if (strlen(global_state.header_buf.buf) == 0 && strlen(global_state.body_buf.buf) == 0) { + if (strlen(curl_state.header_buf.buf) == 0 && strlen(curl_state.body_buf.buf) == 0) { die(STATE_CRITICAL, _("HTTP CRITICAL - No header received from host\n")); } /* get status line of answer, check sanity of HTTP code */ - if (curlhelp_parse_statusline(global_state.header_buf.buf, &global_state.status_line) < 0) { + if (curlhelp_parse_statusline(curl_state.header_buf.buf, &curl_state.status_line) < 0) { snprintf(msg, DEFAULT_BUFFER_SIZE, "Unparsable status line in %.3g seconds response time|%s\n", total_time, perfstring); @@ -1121,12 +1157,12 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat * line */ die(STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x unknown - %s", msg); } - global_state.status_line_initialized = true; + curl_state.status_line_initialized = true; /* get result code from cURL */ long httpReturnCode; handle_curl_option_return_code( - curl_easy_getinfo(global_state.curl, CURLINFO_RESPONSE_CODE, &httpReturnCode), + curl_easy_getinfo(curl_state.curl, CURLINFO_RESPONSE_CODE, &httpReturnCode), "CURLINFO_RESPONSE_CODE"); if (verbose >= 2) { printf("* curl CURLINFO_RESPONSE_CODE is %ld\n", httpReturnCode); @@ -1134,28 +1170,28 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat /* print status line, header, body if verbose */ if (verbose >= 2) { - printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", global_state.header_buf.buf, - (workingState.no_body ? " [[ skipped ]]" : global_state.body_buf.buf)); + printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", curl_state.header_buf.buf, + (workingState.no_body ? " [[ skipped ]]" : curl_state.body_buf.buf)); } /* make sure the status line matches the response we are looking for */ - if (!expected_statuscode(global_state.status_line.first_line, config.server_expect.string)) { + if (!expected_statuscode(curl_state.status_line.first_line, config.server_expect.string)) { if (workingState.serverPort == HTTP_PORT) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), - global_state.status_line.first_line); + curl_state.status_line.first_line); } else { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), - workingState.serverPort, global_state.status_line.first_line); + workingState.serverPort, curl_state.status_line.first_line); } die(STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg, config.show_body ? "\n" : "", - config.show_body ? global_state.body_buf.buf : ""); + config.show_body ? curl_state.body_buf.buf : ""); } mp_state_enum result = STATE_OK; if (config.server_expect.is_present) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), - config.server_expect); + config.server_expect.string); if (verbose) { printf("%s\n", msg); } @@ -1164,7 +1200,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat /* illegal return codes result in a critical state */ if (httpReturnCode >= 600 || httpReturnCode < 100) { die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), - global_state.status_line.http_code, global_state.status_line.msg); + curl_state.status_line.http_code, curl_state.status_line.msg); /* server errors result in a critical state */ } else if (httpReturnCode >= 500) { result = STATE_CRITICAL; @@ -1173,25 +1209,25 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat result = STATE_WARNING; /* check redirected page if specified */ } else if (httpReturnCode >= 300) { - if (config.onredirect == STATE_DEPENDENT) { + if (config.on_redirect_dependent) { if (config.followmethod == FOLLOW_LIBCURL) { - httpReturnCode = global_state.status_line.http_code; + httpReturnCode = curl_state.status_line.http_code; } else { /* old check_http style redirection, if we come * back here, we are in the same status as with * the libcurl method */ - redir_wrapper redir_result = redir(&global_state.header_buf, config, - redir_depth, workingState, global_state); - check_http(config, redir_result.working_state, redir_result.redir_depth, - header_list, redir_result.curl_state); + redir_wrapper redir_result = + redir(&curl_state.header_buf, config, redir_depth, workingState); + cleanup(curl_state); + check_http(config, redir_result.working_state, redir_result.redir_depth); } } else { /* this is a specific code in the command line to * be returned when a redirection is encountered */ } - result = max_state_alt(config.onredirect, result); + result = max_state_alt(config.on_redirect_result_state, result); /* all other codes are considered ok */ } else { result = STATE_OK; @@ -1201,7 +1237,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat /* libcurl redirection internally, handle error states here */ if (config.followmethod == FOLLOW_LIBCURL) { handle_curl_option_return_code( - curl_easy_getinfo(global_state.curl, CURLINFO_REDIRECT_COUNT, &redir_depth), + curl_easy_getinfo(curl_state.curl, CURLINFO_REDIRECT_COUNT, &redir_depth), "CURLINFO_REDIRECT_COUNT"); if (verbose >= 2) { @@ -1216,22 +1252,21 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } /* check status codes, set exit status accordingly */ - if (global_state.status_line.http_code != httpReturnCode) { + if (curl_state.status_line.http_code != httpReturnCode) { die(STATE_CRITICAL, _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"), - string_statuscode(global_state.status_line.http_major, - global_state.status_line.http_minor), - global_state.status_line.http_code, global_state.status_line.msg, httpReturnCode); + string_statuscode(curl_state.status_line.http_major, curl_state.status_line.http_minor), + curl_state.status_line.http_code, curl_state.status_line.msg, httpReturnCode); } if (config.maximum_age >= 0) { result = max_state_alt( - check_document_dates(&global_state.header_buf, &msg, config.maximum_age), result); + check_document_dates(&curl_state.header_buf, &msg, config.maximum_age), result); } /* Page and Header content checks go here */ if (strlen(config.header_expect)) { - if (!strstr(global_state.header_buf.buf, config.header_expect)) { + if (!strstr(curl_state.header_buf.buf, config.header_expect)) { char output_header_search[30] = ""; strncpy(&output_header_search[0], config.header_expect, sizeof(output_header_search)); @@ -1254,7 +1289,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } if (strlen(config.string_expect)) { - if (!strstr(global_state.body_buf.buf, config.string_expect)) { + if (!strstr(curl_state.body_buf.buf, config.string_expect)) { char output_string_search[30] = ""; strncpy(&output_string_search[0], config.string_expect, sizeof(output_string_search)); @@ -1278,7 +1313,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat if (strlen(config.regexp)) { regmatch_t pmatch[REGS]; - int errcode = regexec(&config.compiled_regex, global_state.body_buf.buf, REGS, pmatch, 0); + int errcode = regexec(&config.compiled_regex, curl_state.body_buf.buf, REGS, pmatch, 0); if ((errcode == 0 && !config.invert_regex) || (errcode == REG_NOMATCH && config.invert_regex)) { /* OK - No-op to avoid changing the logic around it */ @@ -1344,11 +1379,10 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat die((int)max_state_alt(result, result_ssl), "HTTP %s: %s %d %s%s%s - %zu bytes in %.3f second response time %s|%s\n%s%s", state_text(result), - string_statuscode(global_state.status_line.http_major, global_state.status_line.http_minor), - global_state.status_line.http_code, global_state.status_line.msg, - strlen(msg) > 0 ? " - " : "", msg, page_len, total_time, - (config.display_html ? "" : ""), perfstring, - (config.show_body ? global_state.body_buf.buf : ""), (config.show_body ? "\n" : "")); + string_statuscode(curl_state.status_line.http_major, curl_state.status_line.http_minor), + curl_state.status_line.http_code, curl_state.status_line.msg, strlen(msg) > 0 ? " - " : "", + msg, page_len, total_time, (config.display_html ? "" : ""), perfstring, + (config.show_body ? curl_state.body_buf.buf : ""), (config.show_body ? "\n" : "")); return max_state_alt(result, result_ssl); } @@ -1375,8 +1409,7 @@ char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) { } redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config config, - int redir_depth, check_curl_working_state working_state, - check_curl_global_state global_state) { + int redir_depth, check_curl_working_state working_state) { curlhelp_statusline status_line; struct phr_header headers[255]; size_t msglen; @@ -1533,8 +1566,6 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config * attached to the URL in Location */ - cleanup(global_state); - redir_wrapper result = { .redir_depth = redir_depth, .working_state = working_state, @@ -1673,7 +1704,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { if (!is_intnonneg(optarg)) { usage2(_("Timeout interval must be a positive integer"), optarg); } else { - result.config.socket_timeout = (int)strtol(optarg, NULL, 10); + result.config.curl_config.socket_timeout = (int)strtol(optarg, NULL, 10); } break; case 'c': /* critical time threshold */ @@ -1724,12 +1755,12 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } break; case 'a': /* authorization info */ - strncpy(result.config.user_auth, optarg, MAX_INPUT_BUFFER - 1); - result.config.user_auth[MAX_INPUT_BUFFER - 1] = 0; + strncpy(result.config.curl_config.user_auth, optarg, MAX_INPUT_BUFFER - 1); + result.config.curl_config.user_auth[MAX_INPUT_BUFFER - 1] = 0; break; case 'b': /* proxy-authorization info */ - strncpy(result.config.proxy_auth, optarg, MAX_INPUT_BUFFER - 1); - result.config.proxy_auth[MAX_INPUT_BUFFER - 1] = 0; + strncpy(result.config.curl_config.proxy_auth, optarg, MAX_INPUT_BUFFER - 1); + result.config.curl_config.proxy_auth[MAX_INPUT_BUFFER - 1] = 0; break; case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */ if (!result.config.initial_config.http_post_data) { @@ -1746,19 +1777,20 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { result.config.initial_config.http_method = strdup(optarg); break; case 'A': /* useragent */ - strncpy(result.config.user_agent, optarg, DEFAULT_BUFFER_SIZE); - result.config.user_agent[DEFAULT_BUFFER_SIZE - 1] = '\0'; + strncpy(result.config.curl_config.user_agent, optarg, DEFAULT_BUFFER_SIZE); + result.config.curl_config.user_agent[DEFAULT_BUFFER_SIZE - 1] = '\0'; break; case 'k': /* Additional headers */ - if (result.config.http_opt_headers_count == 0) { - result.config.http_opt_headers = - malloc(sizeof(char *) * (++result.config.http_opt_headers_count)); + if (result.config.curl_config.http_opt_headers_count == 0) { + result.config.curl_config.http_opt_headers = + malloc(sizeof(char *) * (++result.config.curl_config.http_opt_headers_count)); } else { - result.config.http_opt_headers = - realloc(result.config.http_opt_headers, - sizeof(char *) * (++result.config.http_opt_headers_count)); + result.config.curl_config.http_opt_headers = + realloc(result.config.curl_config.http_opt_headers, + sizeof(char *) * (++result.config.curl_config.http_opt_headers_count)); } - result.config.http_opt_headers[result.config.http_opt_headers_count - 1] = optarg; + result.config.curl_config + .http_opt_headers[result.config.curl_config.http_opt_headers_count - 1] = optarg; break; case 'L': /* show html link */ result.config.display_html = true; @@ -1805,7 +1837,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { usage4(_("Invalid option - SSL is not available")); #endif test_file(optarg); - result.config.client_cert = optarg; + result.config.curl_config.client_cert = optarg; enable_tls = true; break; case 'K': /* use client private key */ @@ -1813,7 +1845,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { usage4(_("Invalid option - SSL is not available")); #endif test_file(optarg); - result.config.client_privkey = optarg; + result.config.curl_config.client_privkey = optarg; enable_tls = true; break; case CA_CERT_OPTION: /* use CA chain file */ @@ -1821,14 +1853,14 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { usage4(_("Invalid option - SSL is not available")); #endif test_file(optarg); - result.config.ca_cert = optarg; + result.config.curl_config.ca_cert = optarg; enable_tls = true; break; case 'D': /* verify peer certificate & host */ #ifndef LIBCURL_FEATURE_SSL usage4(_("Invalid option - SSL is not available")); #endif - result.config.verify_peer_and_host = true; + result.config.curl_config.verify_peer_and_host = true; enable_tls = true; break; case 'S': /* use SSL */ @@ -1852,36 +1884,44 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { break; case 'f': /* onredirect */ if (!strcmp(optarg, "ok")) { - result.config.onredirect = STATE_OK; + result.config.on_redirect_result_state = STATE_OK; + result.config.on_redirect_dependent = false; } else if (!strcmp(optarg, "warning")) { - result.config.onredirect = STATE_WARNING; + result.config.on_redirect_result_state = STATE_WARNING; + result.config.on_redirect_dependent = false; } else if (!strcmp(optarg, "critical")) { - result.config.onredirect = STATE_CRITICAL; + result.config.on_redirect_result_state = STATE_CRITICAL; + result.config.on_redirect_dependent = false; } else if (!strcmp(optarg, "unknown")) { - result.config.onredirect = STATE_UNKNOWN; + result.config.on_redirect_result_state = STATE_UNKNOWN; + result.config.on_redirect_dependent = false; } else if (!strcmp(optarg, "follow")) { - result.config.onredirect = STATE_DEPENDENT; + result.config.on_redirect_dependent = true; } else if (!strcmp(optarg, "stickyport")) { - result.config.onredirect = STATE_DEPENDENT, + result.config.on_redirect_dependent = true; result.config.followmethod = FOLLOW_HTTP_CURL, result.config.followsticky = STICKY_HOST | STICKY_PORT; } else if (!strcmp(optarg, "sticky")) { - result.config.onredirect = STATE_DEPENDENT, + result.config.on_redirect_dependent = true; result.config.followmethod = FOLLOW_HTTP_CURL, result.config.followsticky = STICKY_HOST; } else if (!strcmp(optarg, "follow")) { - result.config.onredirect = STATE_DEPENDENT, + result.config.on_redirect_dependent = true; result.config.followmethod = FOLLOW_HTTP_CURL, result.config.followsticky = STICKY_NONE; } else if (!strcmp(optarg, "curl")) { - result.config.onredirect = STATE_DEPENDENT, + result.config.on_redirect_dependent = true; result.config.followmethod = FOLLOW_LIBCURL; } else { usage2(_("Invalid onredirect option"), optarg); } if (verbose >= 2) { - printf(_("* Following redirects set to %s\n"), - state_text(result.config.onredirect)); + if (result.config.on_redirect_dependent) { + printf(_("* Following redirects\n")); + } else { + printf(_("* Following redirects set to state %s\n"), + state_text(result.config.on_redirect_result_state)); + } } break; case 'd': /* string or substring */ @@ -1898,7 +1938,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { result.config.server_expect.is_present = true; break; case 'T': /* Content-type */ - result.config.http_content_type = strdup(optarg); + result.config.curl_config.http_content_type = strdup(optarg); break; case 'l': /* linespan */ cflags &= ~REG_NEWLINE; @@ -1933,11 +1973,11 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } break; case '4': - result.config.sin_family = AF_INET; + result.config.curl_config.sin_family = AF_INET; break; case '6': #if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) - result.config.sin_family = AF_INET6; + result.config.curl_config.sin_family = AF_INET6; #else usage4(_("IPv6 support not available")); #endif @@ -1997,30 +2037,33 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { result.config.show_body = true; break; case HTTP_VERSION_OPTION: - result.config.curl_http_version = CURL_HTTP_VERSION_NONE; + result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_NONE; if (strcmp(optarg, "1.0") == 0) { - result.config.curl_http_version = CURL_HTTP_VERSION_1_0; + result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_1_0; } else if (strcmp(optarg, "1.1") == 0) { - result.config.curl_http_version = CURL_HTTP_VERSION_1_1; + result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_1_1; } else if ((strcmp(optarg, "2.0") == 0) || (strcmp(optarg, "2") == 0)) { #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) - result.config.curl_http_version = CURL_HTTP_VERSION_2_0; + result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_2_0; #else result.config.curl_http_version = CURL_HTTP_VERSION_NONE; #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) */ + } else if ((strcmp(optarg, "3") == 0)) { + // TODO find out which libcurl version starts supporting HTTP 3 + result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_3; } else { fprintf(stderr, "unknown http-version parameter: %s\n", optarg); exit(STATE_WARNING); } break; case AUTOMATIC_DECOMPRESSION: - result.config.automatic_decompression = true; + result.config.curl_config.automatic_decompression = true; break; case COOKIE_JAR: - result.config.cookie_jar_file = optarg; + result.config.curl_config.cookie_jar_file = optarg; break; case HAPROXY_PROTOCOL: - result.config.haproxy_protocol = true; + result.config.curl_config.haproxy_protocol = true; break; case '?': /* print short usage statement if args not parsable */ @@ -2035,7 +2078,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { /* ssl_version initialized to CURL_SSLVERSION_DEFAULT as a default. * Only set if it's non-zero. This helps when we include multiple * parameters, like -S and -C combinations */ - result.config.ssl_version = CURL_SSLVERSION_DEFAULT; + result.config.curl_config.ssl_version = CURL_SSLVERSION_DEFAULT; if (tls_option_optarg != NULL) { char *plus_ptr = strchr(optarg, '+'); if (plus_ptr) { @@ -2044,30 +2087,30 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } if (optarg[0] == '2') { - result.config.ssl_version = CURL_SSLVERSION_SSLv2; + result.config.curl_config.ssl_version = CURL_SSLVERSION_SSLv2; } else if (optarg[0] == '3') { - result.config.ssl_version = CURL_SSLVERSION_SSLv3; + result.config.curl_config.ssl_version = CURL_SSLVERSION_SSLv3; } else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0")) { #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) - result.config.ssl_version = CURL_SSLVERSION_TLSv1_0; + result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_0; #else result.config.ssl_version = CURL_SSLVERSION_DEFAULT; #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ } else if (!strcmp(optarg, "1.1")) { #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) - result.config.ssl_version = CURL_SSLVERSION_TLSv1_1; + result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_1; #else result.config.ssl_version = CURL_SSLVERSION_DEFAULT; #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ } else if (!strcmp(optarg, "1.2")) { #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) - result.config.ssl_version = CURL_SSLVERSION_TLSv1_2; + result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_2; #else result.config.ssl_version = CURL_SSLVERSION_DEFAULT; #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ } else if (!strcmp(optarg, "1.3")) { #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) - result.config.ssl_version = CURL_SSLVERSION_TLSv1_3; + result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_3; #else result.config.ssl_version = CURL_SSLVERSION_DEFAULT; #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */ @@ -2078,35 +2121,35 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) if (got_plus) { - switch (result.config.ssl_version) { + switch (result.config.curl_config.ssl_version) { case CURL_SSLVERSION_TLSv1_3: - result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; + result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; break; case CURL_SSLVERSION_TLSv1_2: case CURL_SSLVERSION_TLSv1_1: case CURL_SSLVERSION_TLSv1_0: - result.config.ssl_version |= CURL_SSLVERSION_MAX_DEFAULT; + result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_DEFAULT; break; } } else { - switch (result.config.ssl_version) { + switch (result.config.curl_config.ssl_version) { case CURL_SSLVERSION_TLSv1_3: - result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; + result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; break; case CURL_SSLVERSION_TLSv1_2: - result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2; + result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2; break; case CURL_SSLVERSION_TLSv1_1: - result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1; + result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1; break; case CURL_SSLVERSION_TLSv1_0: - result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0; + result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0; break; } } #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */ if (verbose >= 2) { - printf(_("* Set SSL/TLS version to %d\n"), result.config.ssl_version); + printf(_("* Set SSL/TLS version to %d\n"), result.config.curl_config.ssl_version); } if (!specify_port) { result.config.initial_config.serverPort = HTTPS_PORT; @@ -2135,18 +2178,18 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { set_thresholds(&result.config.thlds, warning_thresholds, critical_thresholds); if (critical_thresholds && - result.config.thlds->critical->end > (double)result.config.socket_timeout) { - result.config.socket_timeout = (int)result.config.thlds->critical->end + 1; + result.config.thlds->critical->end > (double)result.config.curl_config.socket_timeout) { + result.config.curl_config.socket_timeout = (int)result.config.thlds->critical->end + 1; } if (verbose >= 2) { - printf("* Socket timeout set to %ld seconds\n", result.config.socket_timeout); + printf("* Socket timeout set to %ld seconds\n", result.config.curl_config.socket_timeout); } if (result.config.initial_config.http_method == NULL) { result.config.initial_config.http_method = strdup("GET"); } - if (result.config.client_cert && !result.config.client_privkey) { + if (result.config.curl_config.client_cert && !result.config.curl_config.client_privkey) { usage4(_("If you use a client certificate you must also specify a private key file")); } diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index 7566b19c..be25d1bb 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -65,27 +65,32 @@ check_curl_working_state check_curl_working_state_init() { } typedef struct { - check_curl_working_state initial_config; - sa_family_t sin_family; - bool automatic_decompression; bool haproxy_protocol; + long socket_timeout; + sa_family_t sin_family; + int curl_http_version; + char **http_opt_headers; + size_t http_opt_headers_count; + int ssl_version; char *client_cert; char *client_privkey; char *ca_cert; - int ssl_version; + bool verify_peer_and_host; char user_agent[DEFAULT_BUFFER_SIZE]; - char **http_opt_headers; - size_t http_opt_headers_count; - int max_depth; - char *http_content_type; - long socket_timeout; - char user_auth[MAX_INPUT_BUFFER]; char proxy_auth[MAX_INPUT_BUFFER]; + char user_auth[MAX_INPUT_BUFFER]; + char *http_content_type; + char *cookie_jar_file; +} check_curl_static_curl_config; + +typedef struct { + check_curl_working_state initial_config; + + check_curl_static_curl_config curl_config; + int max_depth; int followmethod; int followsticky; - int curl_http_version; - char *cookie_jar_file; int maximum_age; @@ -97,7 +102,6 @@ typedef struct { mp_state_enum state_regex; bool invert_regex; - bool verify_peer_and_host; bool check_cert; bool continue_after_check_cert; int days_till_exp_warn; @@ -111,7 +115,8 @@ typedef struct { } server_expect; char string_expect[MAX_INPUT_BUFFER]; char header_expect[MAX_INPUT_BUFFER]; - mp_state_enum onredirect; + mp_state_enum on_redirect_result_state; + bool on_redirect_dependent; bool show_extended_perfdata; bool show_body; @@ -122,33 +127,35 @@ check_curl_config check_curl_config_init() { check_curl_config tmp = { .initial_config = check_curl_working_state_init(), - .sin_family = AF_UNSPEC, - - .automatic_decompression = false, - .haproxy_protocol = false, - .client_cert = NULL, - .client_privkey = NULL, - .ca_cert = NULL, - .ssl_version = CURL_SSLVERSION_DEFAULT, - .user_agent = {'\0'}, - .http_opt_headers = NULL, - .http_opt_headers_count = 0, + .curl_config = + { + .automatic_decompression = false, + .socket_timeout = DEFAULT_SOCKET_TIMEOUT, + .haproxy_protocol = false, + .sin_family = AF_UNSPEC, + .curl_http_version = CURL_HTTP_VERSION_NONE, + .http_opt_headers = NULL, + .http_opt_headers_count = 0, + .ssl_version = CURL_SSLVERSION_DEFAULT, + .client_cert = NULL, + .client_privkey = NULL, + .ca_cert = NULL, + .verify_peer_and_host = false, + .user_agent = {'\0'}, + .proxy_auth = "", + .user_auth = "", + .http_content_type = NULL, + .cookie_jar_file = NULL, + }, .max_depth = DEFAULT_MAX_REDIRS, - .http_content_type = NULL, - .socket_timeout = DEFAULT_SOCKET_TIMEOUT, - .user_auth = "", - .proxy_auth = "", .followmethod = FOLLOW_HTTP_CURL, .followsticky = STICKY_NONE, - .curl_http_version = CURL_HTTP_VERSION_NONE, - .cookie_jar_file = NULL, .maximum_age = -1, .regexp = {}, .compiled_regex = {}, .state_regex = STATE_CRITICAL, .invert_regex = false, - .verify_peer_and_host = false, .check_cert = false, .continue_after_check_cert = false, .days_till_exp_warn = 0, @@ -163,11 +170,16 @@ check_curl_config check_curl_config_init() { }, .string_expect = "", .header_expect = "", - .onredirect = STATE_OK, + .on_redirect_result_state = STATE_OK, + .on_redirect_dependent = true, .show_extended_perfdata = false, .show_body = false, .display_html = false, }; + + snprintf(tmp.curl_config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", + "check_curl", NP_VERSION, VERSION, curl_version()); + return tmp; } -- cgit v1.2.3-74-g34f1 From 684602ddec1ae3c136eec47aa078aa3fc69bbbcf Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 11 Sep 2025 14:44:17 +0200 Subject: check_curl: http3 is only available with libcurl 7.66 or later --- plugins/check_curl.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index f3cf7422..7f3bcdb1 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -2049,8 +2049,11 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { result.config.curl_http_version = CURL_HTTP_VERSION_NONE; #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) */ } else if ((strcmp(optarg, "3") == 0)) { - // TODO find out which libcurl version starts supporting HTTP 3 +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 66, 0) result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_3; +#else + result.config.curl_http_version = CURL_HTTP_VERSION_NONE; +#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 66, 0) */ } else { fprintf(stderr, "unknown http-version parameter: %s\n", optarg); exit(STATE_WARNING); -- cgit v1.2.3-74-g34f1 From ab66b41d235c3ec38eeb8dfd2091e3b76fc1ffa3 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 12 Sep 2025 01:14:14 +0200 Subject: check_curl: create outsourced helpers in extra files --- plugins/Makefile.am | 2 + plugins/check_curl.c | 1312 ++--------------------------- plugins/check_curl.d/check_curl_helpers.c | 1217 ++++++++++++++++++++++++++ plugins/check_curl.d/check_curl_helpers.h | 125 +++ plugins/check_curl.d/config.h | 76 +- 5 files changed, 1397 insertions(+), 1335 deletions(-) create mode 100644 plugins/check_curl.d/check_curl_helpers.c create mode 100644 plugins/check_curl.d/check_curl_helpers.h (limited to 'plugins/check_curl.c') diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 1f24d923..1a9399f0 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -66,6 +66,7 @@ EXTRA_DIST = t \ check_hpjd.d \ check_game.d \ check_radius.d \ + check_curl.d \ check_disk.d \ check_time.d \ check_users.d \ @@ -134,6 +135,7 @@ check_cluster_LDADD = $(BASEOBJS) check_curl_CFLAGS = $(AM_CFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser check_curl_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser check_curl_LDADD = $(NETLIBS) $(LIBCURLLIBS) $(SSLOBJS) $(URIPARSERLIBS) picohttpparser/libpicohttpparser.a +check_curl_SOURCES = check_curl.c check_curl.d/check_curl_helpers.c check_dbi_LDADD = $(NETLIBS) $(DBILIBS) check_dig_LDADD = $(NETLIBS) check_disk_LDADD = $(BASEOBJS) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 7f3bcdb1..b747ec99 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -45,6 +45,7 @@ const char *email = "devel@monitoring-plugins.org"; #include "common.h" #include "utils.h" +#include "./check_curl.d/check_curl_helpers.h" #ifndef LIBCURL_PROTOCOL_HTTP # error libcurl compiled without HTTP support, compiling check_curl plugin does not makes a lot of sense @@ -53,8 +54,6 @@ const char *email = "devel@monitoring-plugins.org"; #include "curl/curl.h" #include "curl/easy.h" -#include "./picohttpparser/picohttpparser.h" - #include "uriparser/Uri.h" #include @@ -66,48 +65,10 @@ const char *email = "devel@monitoring-plugins.org"; #include -#define MAKE_LIBCURL_VERSION(major, minor, patch) ((major) * 0x10000 + (minor) * 0x100 + (patch)) - -#define INET_ADDR_MAX_SIZE INET6_ADDRSTRLEN enum { MAX_IPV4_HOSTLENGTH = 255, }; -/* for buffers for header and body */ -typedef struct { - char *buf; - size_t buflen; - size_t bufsize; -} curlhelp_write_curlbuf; - -/* for buffering the data sent in PUT */ -typedef struct { - char *buf; - size_t buflen; - off_t pos; -} curlhelp_read_curlbuf; - -/* for parsing the HTTP status line */ -typedef struct { - int http_major; /* major version of the protocol, always 1 (HTTP/0.9 - * never reached the big internet most likely) */ - int http_minor; /* minor version of the protocol, usually 0 or 1 */ - int http_code; /* HTTP return code as in RFC 2145 */ - int http_subcode; /* Microsoft IIS extension, HTTP subcodes, see - * http://support.microsoft.com/kb/318380/en-us */ - const char *msg; /* the human readable message */ - char *first_line; /* a copy of the first line */ -} curlhelp_statusline; - -/* to know the underlying SSL library used by libcurl */ -typedef enum curlhelp_ssl_library { - CURLHELP_SSL_LIBRARY_UNKNOWN, - CURLHELP_SSL_LIBRARY_OPENSSL, - CURLHELP_SSL_LIBRARY_LIBRESSL, - CURLHELP_SSL_LIBRARY_GNUTLS, - CURLHELP_SSL_LIBRARY_NSS -} curlhelp_ssl_library; - enum { REGS = 2, }; @@ -115,33 +76,12 @@ enum { #include "regex.h" // Globals -static int verbose = 0; +int verbose = 0; -typedef struct { - bool curl_global_initialized; - bool curl_easy_initialized; - bool body_buf_initialized; - curlhelp_write_curlbuf body_buf; - bool header_buf_initialized; - curlhelp_write_curlbuf header_buf; - bool status_line_initialized; - curlhelp_statusline status_line; - bool put_buf_initialized; - curlhelp_read_curlbuf put_buf; - CURL *curl; - - struct curl_slist *header_list; - struct curl_slist *host; -} check_curl_global_state; - -static char errbuf[MAX_INPUT_BUFFER]; -static char msg[DEFAULT_BUFFER_SIZE]; -typedef union { - struct curl_slist *to_info; - struct curl_certinfo *to_certinfo; -} cert_ptr_union; -static bool is_openssl_callback = false; -static bool add_sslctx_verify_fun = false; +extern char msg[DEFAULT_BUFFER_SIZE]; +extern char errbuf[MAX_INPUT_BUFFER]; +extern bool is_openssl_callback; +extern bool add_sslctx_verify_fun; #if defined(HAVE_SSL) && defined(USE_OPENSSL) static X509 *cert = NULL; @@ -153,7 +93,6 @@ typedef struct { } check_curl_config_wrapper; static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); -static void handle_curl_option_return_code(CURLcode res, const char *option); static mp_state_enum check_http(check_curl_config /*config*/, check_curl_working_state workingState, int redir_depth); @@ -166,590 +105,22 @@ typedef struct { static redir_wrapper redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/, int redir_depth, check_curl_working_state working_state); -static char *perfd_time(double elapsed_time, thresholds * /*thlds*/, long /*socket_timeout*/); -static char *perfd_time_connect(double elapsed_time_connect, long /*socket_timeout*/); -static char *perfd_time_ssl(double elapsed_time_ssl, long /*socket_timeout*/); -static char *perfd_time_firstbyte(double elapsed_time_firstbyte, long /*socket_timeout*/); -static char *perfd_time_headers(double elapsed_time_headers, long /*socket_timeout*/); -static char *perfd_time_transfer(double elapsed_time_transfer, long /*socket_timeout*/); -static char *perfd_size(size_t page_len, int /*min_page_len*/); - static void print_help(void); void print_usage(void); static void print_curl_version(void); -static int curlhelp_initwritebuffer(curlhelp_write_curlbuf * /*buf*/); -static size_t curlhelp_buffer_write_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, - void * /*stream*/); -static void curlhelp_freewritebuffer(curlhelp_write_curlbuf * /*buf*/); - -static int curlhelp_initreadbuffer(curlhelp_read_curlbuf * /*buf*/, const char * /*data*/, - size_t /*datalen*/); -static size_t curlhelp_buffer_read_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, - void * /*stream*/); -static void curlhelp_freereadbuffer(curlhelp_read_curlbuf * /*buf*/); - -static curlhelp_ssl_library curlhelp_get_ssl_library(void); -static const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library /*ssl_library*/); - -int net_noopenssl_check_certificate(cert_ptr_union *, int, int); - -static int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * /*status_line*/); -static void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/); - -static char *get_header_value(const struct phr_header *headers, size_t nof_headers, - const char *header); -static mp_state_enum check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, - char (*msg)[DEFAULT_BUFFER_SIZE], int /*maximum_age*/); -static size_t get_content_length(const curlhelp_write_curlbuf *header_buf, - const curlhelp_write_curlbuf *body_buf); -static int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family); - // typedef struct { // int errorcode; // } check_curl_evaluation_wrapper; // check_curl_evaluation_wrapper check_curl_evaluate(check_curl_config config, // mp_check overall[static 1]) {} -CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm); - -typedef struct { - int errorcode; - check_curl_global_state curl_state; -} check_curl_configure_curl_wrapper; -check_curl_configure_curl_wrapper -check_curl_configure_curl(const check_curl_static_curl_config config, - check_curl_working_state working_state, bool check_cert, - bool on_redirect_dependent, int follow_method, int max_depth) { - check_curl_configure_curl_wrapper result = { - .errorcode = OK, - .curl_state = - { - .curl_global_initialized = false, - .curl_easy_initialized = false, - .curl = NULL, - - .body_buf_initialized = false, - .body_buf = {}, - .header_buf_initialized = false, - .header_buf = {}, - .status_line_initialized = false, - .status_line = {}, - .put_buf_initialized = false, - .put_buf = {}, - - .header_list = NULL, - .host = NULL, - }, - }; - - if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { - die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n"); - } - result.curl_state.curl_global_initialized = true; - - if ((result.curl_state.curl = curl_easy_init()) == NULL) { - die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n"); - } - result.curl_state.curl_easy_initialized = true; - - if (verbose >= 1) { - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_VERBOSE, 1), - "CURLOPT_VERBOSE"); - } - /* print everything on stdout like check_http would do */ - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_STDERR, stdout), - "CURLOPT_STDERR"); - - if (config.automatic_decompression) { -#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_ACCEPT_ENCODING, ""), - "CURLOPT_ACCEPT_ENCODING"); -#else - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING"); -#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */ - } - - /* initialize buffer for body of the answer */ - if (curlhelp_initwritebuffer(&result.curl_state.body_buf) < 0) { - die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n"); - } - result.curl_state.body_buf_initialized = true; - - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEFUNCTION, - curlhelp_buffer_write_callback), - "CURLOPT_WRITEFUNCTION"); - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEDATA, - (void *)&result.curl_state.body_buf), - "CURLOPT_WRITEDATA"); - - /* initialize buffer for header of the answer */ - if (curlhelp_initwritebuffer(&result.curl_state.header_buf) < 0) { - die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n"); - } - result.curl_state.header_buf_initialized = true; - - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_HEADERFUNCTION, - curlhelp_buffer_write_callback), - "CURLOPT_HEADERFUNCTION"); - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEHEADER, - (void *)&result.curl_state.header_buf), - "CURLOPT_WRITEHEADER"); - - /* set the error buffer */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_ERRORBUFFER, errbuf), - "CURLOPT_ERRORBUFFER"); - - /* set timeouts */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_CONNECTTIMEOUT, config.socket_timeout), - "CURLOPT_CONNECTTIMEOUT"); - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_TIMEOUT, config.socket_timeout), - "CURLOPT_TIMEOUT"); - - /* enable haproxy protocol */ - if (config.haproxy_protocol) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_HAPROXYPROTOCOL, 1L), - "CURLOPT_HAPROXYPROTOCOL"); - } - - // fill dns resolve cache to make curl connect to the given server_address instead of the - // host_name, only required for ssl, because we use the host_name later on to make SNI happy - char dnscache[DEFAULT_BUFFER_SIZE]; - char addrstr[DEFAULT_BUFFER_SIZE / 2]; - if (working_state.use_ssl && working_state.host_name != NULL) { - int res; - if ((res = lookup_host(working_state.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2, - config.sin_family)) != 0) { - snprintf(msg, DEFAULT_BUFFER_SIZE, - _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), - working_state.server_address, res, gai_strerror(res)); - die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); - } - snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", working_state.host_name, - working_state.serverPort, addrstr); - result.curl_state.host = curl_slist_append(NULL, dnscache); - curl_easy_setopt(result.curl_state.curl, CURLOPT_RESOLVE, result.curl_state.host); - if (verbose >= 1) { - printf("* curl CURLOPT_RESOLVE: %s\n", dnscache); - } - } - - // If server_address is an IPv6 address it must be surround by square brackets - struct in6_addr tmp_in_addr; - if (inet_pton(AF_INET6, working_state.server_address, &tmp_in_addr) == 1) { - char *new_server_address = malloc(strlen(working_state.server_address) + 3); - if (new_server_address == NULL) { - die(STATE_UNKNOWN, "HTTP UNKNOWN - Unable to allocate memory\n"); - } - snprintf(new_server_address, strlen(working_state.server_address) + 3, "[%s]", - working_state.server_address); - working_state.server_address = new_server_address; - } - - /* compose URL: use the address we want to connect to, set Host: header later */ - char url[DEFAULT_BUFFER_SIZE]; - snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", working_state.use_ssl ? "https" : "http", - (working_state.use_ssl & (working_state.host_name != NULL)) - ? working_state.host_name - : working_state.server_address, - working_state.serverPort, working_state.server_url); - - if (verbose >= 1) { - printf("* curl CURLOPT_URL: %s\n", url); - } - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, url), - "CURLOPT_URL"); - - /* extract proxy information for legacy proxy https requests */ - if (!strcmp(working_state.http_method, "CONNECT") || - strstr(working_state.server_url, "http") == working_state.server_url) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXY, working_state.server_address), - "CURLOPT_PROXY"); - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXYPORT, - (long)working_state.serverPort), - "CURLOPT_PROXYPORT"); - if (verbose >= 2) { - printf("* curl CURLOPT_PROXY: %s:%d\n", working_state.server_address, - working_state.serverPort); - } - working_state.http_method = "GET"; - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, working_state.server_url), - "CURLOPT_URL"); - } - - /* disable body for HEAD request */ - if (working_state.http_method && !strcmp(working_state.http_method, "HEAD")) { - working_state.no_body = true; - } - - /* set HTTP protocol version */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_HTTP_VERSION, config.curl_http_version), - "CURLOPT_HTTP_VERSION"); - - /* set HTTP method */ - if (working_state.http_method) { - if (!strcmp(working_state.http_method, "POST")) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_POST, 1), "CURLOPT_POST"); - } else if (!strcmp(working_state.http_method, "PUT")) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD"); - } else { - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, - CURLOPT_CUSTOMREQUEST, - working_state.http_method), - "CURLOPT_CUSTOMREQUEST"); - } - } - - char *force_host_header = NULL; - /* check if Host header is explicitly set in options */ - if (config.http_opt_headers_count) { - for (size_t i = 0; i < config.http_opt_headers_count; i++) { - if (strncmp(config.http_opt_headers[i], "Host:", 5) == 0) { - force_host_header = config.http_opt_headers[i]; - } - } - } - - /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in - * anyway */ - char http_header[DEFAULT_BUFFER_SIZE]; - if (working_state.host_name != NULL && force_host_header == NULL) { - if ((working_state.virtualPort != HTTP_PORT && !working_state.use_ssl) || - (working_state.virtualPort != HTTPS_PORT && working_state.use_ssl)) { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", working_state.host_name, - working_state.virtualPort); - } else { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", working_state.host_name); - } - result.curl_state.header_list = - curl_slist_append(result.curl_state.header_list, http_header); - } - - /* always close connection, be nice to servers */ - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Connection: close"); - result.curl_state.header_list = curl_slist_append(result.curl_state.header_list, http_header); - - /* attach additional headers supplied by the user */ - /* optionally send any other header tag */ - if (config.http_opt_headers_count) { - for (size_t i = 0; i < config.http_opt_headers_count; i++) { - result.curl_state.header_list = - curl_slist_append(result.curl_state.header_list, config.http_opt_headers[i]); - } - } - - /* set HTTP headers */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_HTTPHEADER, result.curl_state.header_list), - "CURLOPT_HTTPHEADER"); - -#ifdef LIBCURL_FEATURE_SSL - /* set SSL version, warn about insecure or unsupported versions */ - if (working_state.use_ssl) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLVERSION, config.ssl_version), - "CURLOPT_SSLVERSION"); - } - - /* client certificate and key to present to server (SSL) */ - if (config.client_cert) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLCERT, config.client_cert), - "CURLOPT_SSLCERT"); - } - - if (config.client_privkey) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLKEY, config.client_privkey), - "CURLOPT_SSLKEY"); - } - - if (config.ca_cert) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_CAINFO, config.ca_cert), - "CURLOPT_CAINFO"); - } - - if (config.ca_cert || config.verify_peer_and_host) { - /* per default if we have a CA verify both the peer and the - * hostname in the certificate, can be switched off later */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 1), - "CURLOPT_SSL_VERIFYPEER"); - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 2), - "CURLOPT_SSL_VERIFYHOST"); - } else { - /* backward-compatible behaviour, be tolerant in checks - * TODO: depending on more options have aspects we want - * to be less tolerant about ssl verfications - */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 0), - "CURLOPT_SSL_VERIFYPEER"); - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 0), - "CURLOPT_SSL_VERIFYHOST"); - } - - /* detect SSL library used by libcurl */ - curlhelp_ssl_library ssl_library = curlhelp_get_ssl_library(); - - /* try hard to get a stack of certificates to verify against */ - if (check_cert) { -# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) - /* inform curl to report back certificates */ - switch (ssl_library) { - case CURLHELP_SSL_LIBRARY_OPENSSL: - case CURLHELP_SSL_LIBRARY_LIBRESSL: - /* set callback to extract certificate with OpenSSL context function (works with - * OpenSSL-style libraries only!) */ -# ifdef USE_OPENSSL - /* libcurl and monitoring plugins built with OpenSSL, good */ - add_sslctx_verify_fun = true; - is_openssl_callback = true; -# endif /* USE_OPENSSL */ - /* libcurl is built with OpenSSL, monitoring plugins, so falling - * back to manually extracting certificate information */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); - break; - - case CURLHELP_SSL_LIBRARY_NSS: -# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) - /* NSS: support for CERTINFO is implemented since 7.34.0 */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); -# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ - die(STATE_CRITICAL, - "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library " - "'%s' is too old)\n", - curlhelp_get_ssl_library_string(ssl_library)); -# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ - break; - - case CURLHELP_SSL_LIBRARY_GNUTLS: -# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) - /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); -# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */ - die(STATE_CRITICAL, - "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library " - "'%s' is too old)\n", - curlhelp_get_ssl_library_string(ssl_library)); -# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */ - break; - - case CURLHELP_SSL_LIBRARY_UNKNOWN: - default: - die(STATE_CRITICAL, - "HTTP CRITICAL - Cannot retrieve certificates (unknown SSL library '%s', must " - "implement first)\n", - curlhelp_get_ssl_library_string(ssl_library)); - break; - } -# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ - /* old libcurl, our only hope is OpenSSL, otherwise we are out of luck */ - if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL || - ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL) { - add_sslctx_verify_fun = true; - } else { - die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no " - "CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl " - "too old and has no CURLOPT_CERTINFO)\n"); - } -# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ - } - -# if LIBCURL_VERSION_NUM >= \ - MAKE_LIBCURL_VERSION(7, 10, 6) /* required for CURLOPT_SSL_CTX_FUNCTION */ - // ssl ctx function is not available with all ssl backends - if (curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_CTX_FUNCTION, NULL) != - CURLE_UNKNOWN_OPTION) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), - "CURLOPT_SSL_CTX_FUNCTION"); - } -# endif -#endif /* LIBCURL_FEATURE_SSL */ - - /* set default or user-given user agent identification */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_USERAGENT, config.user_agent), - "CURLOPT_USERAGENT"); - - /* proxy-authentication */ - if (strcmp(config.proxy_auth, "")) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXYUSERPWD, config.proxy_auth), - "CURLOPT_PROXYUSERPWD"); - } - - /* authentication */ - if (strcmp(config.user_auth, "")) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_USERPWD, config.user_auth), - "CURLOPT_USERPWD"); - } - /* TODO: parameter auth method, bitfield of following methods: - * CURLAUTH_BASIC (default) - * CURLAUTH_DIGEST - * CURLAUTH_DIGEST_IE - * CURLAUTH_NEGOTIATE - * CURLAUTH_NTLM - * CURLAUTH_NTLM_WB - * - * convenience tokens for typical sets of methods: - * CURLAUTH_ANYSAFE: most secure, without BASIC - * or CURLAUTH_ANY: most secure, even BASIC if necessary - * - * handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPAUTH, - * (long)CURLAUTH_DIGEST ), "CURLOPT_HTTPAUTH"); - */ - - /* handle redirections */ - if (on_redirect_dependent) { - if (follow_method == FOLLOW_LIBCURL) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_FOLLOWLOCATION, 1), - "CURLOPT_FOLLOWLOCATION"); - - /* default -1 is infinite, not good, could lead to zombie plugins! - Setting it to one bigger than maximal limit to handle errors nicely below - */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_MAXREDIRS, max_depth + 1), - "CURLOPT_MAXREDIRS"); - - /* for now allow only http and https (we are a http(s) check plugin in the end) */ -#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 85, 0) - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"), - "CURLOPT_REDIR_PROTOCOLS_STR"); -#elif LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4) - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, - CURLOPT_REDIR_PROTOCOLS, - CURLPROTO_HTTP | CURLPROTO_HTTPS), - "CURLOPT_REDIRECT_PROTOCOLS"); -#endif - - /* TODO: handle the following aspects of redirection, make them - * command line options too later: - CURLOPT_POSTREDIR: method switch - CURLINFO_REDIRECT_URL: custom redirect option - CURLOPT_REDIRECT_PROTOCOLS: allow people to step outside safe protocols - CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range - option here is nice like for expected page size? - */ - } else { - /* old style redirection*/ - } - } - /* no-body */ - if (working_state.no_body) { - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_NOBODY, 1), - "CURLOPT_NOBODY"); - } - - /* IPv4 or IPv6 forced DNS resolution */ - if (config.sin_family == AF_UNSPEC) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), - "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)"); - } else if (config.sin_family == AF_INET) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), - "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)"); - } -#if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) - else if (config.sin_family == AF_INET6) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), - "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)"); - } -#endif - - /* either send http POST data (any data, not only POST)*/ - if (!strcmp(working_state.http_method, "POST") || !strcmp(working_state.http_method, "PUT")) { - /* set content of payload for POST and PUT */ - if (config.http_content_type) { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", - config.http_content_type); - result.curl_state.header_list = - curl_slist_append(result.curl_state.header_list, http_header); - } - /* NULL indicates "HTTP Continue" in libcurl, provide an empty string - * in case of no POST/PUT data */ - if (!working_state.http_post_data) { - working_state.http_post_data = ""; - } - - if (!strcmp(working_state.http_method, "POST")) { - /* POST method, set payload with CURLOPT_POSTFIELDS */ - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, - CURLOPT_POSTFIELDS, - working_state.http_post_data), - "CURLOPT_POSTFIELDS"); - } else if (!strcmp(working_state.http_method, "PUT")) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_READFUNCTION, - (curl_read_callback)curlhelp_buffer_read_callback), - "CURLOPT_READFUNCTION"); - if (curlhelp_initreadbuffer(&result.curl_state.put_buf, working_state.http_post_data, - strlen(working_state.http_post_data)) < 0) { - die(STATE_UNKNOWN, - "HTTP CRITICAL - out of memory allocating read buffer for PUT\n"); - } - result.curl_state.put_buf_initialized = true; - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, - CURLOPT_READDATA, - (void *)&result.curl_state.put_buf), - "CURLOPT_READDATA"); - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_INFILESIZE, - (curl_off_t)strlen(working_state.http_post_data)), - "CURLOPT_INFILESIZE"); - } - } - - /* cookie handling */ - if (config.cookie_jar_file != NULL) { - /* enable reading cookies from a file, and if the filename is an empty string, only - * enable the curl cookie engine */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_COOKIEFILE, config.cookie_jar_file), - "CURLOPT_COOKIEFILE"); - /* now enable saving cookies to a file, but only if the filename is not an empty string, - * since writing it would fail */ - if (*config.cookie_jar_file) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_COOKIEJAR, config.cookie_jar_file), - "CURLOPT_COOKIEJAR"); - } - } - return result; -} - #if defined(HAVE_SSL) && defined(USE_OPENSSL) mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit); #endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ -static void test_file(char * /*path*/); - int main(int argc, char **argv) { setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); @@ -805,7 +176,11 @@ int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { } return 1; } +# endif /* USE_OPENSSL */ +#endif /* HAVE_SSL */ +#ifdef HAVE_SSL +# ifdef USE_OPENSSL CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) { (void)curl; // ignore unused parameter (void)parm; // ignore unused parameter @@ -825,151 +200,6 @@ CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) { # endif /* USE_OPENSSL */ #endif /* HAVE_SSL */ -/* returns a string "HTTP/1.x" or "HTTP/2" */ -static char *string_statuscode(int major, int minor) { - static char buf[10]; - - switch (major) { - case 1: - snprintf(buf, sizeof(buf), "HTTP/%d.%d", major, minor); - break; - case 2: - case 3: - snprintf(buf, sizeof(buf), "HTTP/%d", major); - break; - default: - /* assuming here HTTP/N with N>=4 */ - snprintf(buf, sizeof(buf), "HTTP/%d", major); - break; - } - - return buf; -} - -/* Checks if the server 'reply' is one of the expected 'statuscodes' */ -static bool expected_statuscode(const char *reply, const char *statuscodes) { - char *expected; - - if ((expected = strdup(statuscodes)) == NULL) { - die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); - } - - bool result = false; - for (char *code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) { - if (strstr(reply, code) != NULL) { - result = true; - break; - } - } - - free(expected); - return result; -} - -void handle_curl_option_return_code(CURLcode res, const char *option) { - if (res != CURLE_OK) { - snprintf(msg, DEFAULT_BUFFER_SIZE, - _("Error while setting cURL option '%s': cURL returned %d - %s"), option, res, - curl_easy_strerror(res)); - die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); - } -} - -int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family) { - struct addrinfo hints = { - .ai_family = addr_family, - .ai_socktype = SOCK_STREAM, - .ai_flags = AI_CANONNAME, - }; - - struct addrinfo *result; - int errcode = getaddrinfo(host, NULL, &hints, &result); - if (errcode != 0) { - return errcode; - } - - strcpy(buf, ""); - struct addrinfo *res = result; - - size_t buflen_remaining = buflen - 1; - size_t addrstr_len; - char addrstr[100]; - void *ptr = {0}; - while (res) { - switch (res->ai_family) { - case AF_INET: - ptr = &((struct sockaddr_in *)res->ai_addr)->sin_addr; - break; - case AF_INET6: - ptr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; - break; - } - - inet_ntop(res->ai_family, ptr, addrstr, 100); - if (verbose >= 1) { - printf("* getaddrinfo IPv%d address: %s\n", res->ai_family == PF_INET6 ? 6 : 4, - addrstr); - } - - // Append all IPs to buf as a comma-separated string - addrstr_len = strlen(addrstr); - if (buflen_remaining > addrstr_len + 1) { - if (buf[0] != '\0') { - strncat(buf, ",", buflen_remaining); - buflen_remaining -= 1; - } - strncat(buf, addrstr, buflen_remaining); - buflen_remaining -= addrstr_len; - } - - res = res->ai_next; - } - - freeaddrinfo(result); - - return 0; -} - -static void cleanup(check_curl_global_state global_state) { - if (global_state.status_line_initialized) { - curlhelp_free_statusline(&global_state.status_line); - } - global_state.status_line_initialized = false; - - if (global_state.curl_easy_initialized) { - curl_easy_cleanup(global_state.curl); - } - global_state.curl_easy_initialized = false; - - if (global_state.curl_global_initialized) { - curl_global_cleanup(); - } - global_state.curl_global_initialized = false; - - if (global_state.body_buf_initialized) { - curlhelp_freewritebuffer(&global_state.body_buf); - } - global_state.body_buf_initialized = false; - - if (global_state.header_buf_initialized) { - curlhelp_freewritebuffer(&global_state.header_buf); - } - global_state.header_buf_initialized = false; - - if (global_state.put_buf_initialized) { - curlhelp_freereadbuffer(&global_state.put_buf); - } - global_state.put_buf_initialized = false; - - if (global_state.header_list) { - curl_slist_free_all(global_state.header_list); - } - - if (global_state.host) { - curl_slist_free_all(global_state.host); - } -} - mp_state_enum check_http(const check_curl_config config, check_curl_working_state workingState, int redir_depth) { @@ -981,6 +211,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat config.followmethod, config.max_depth); check_curl_global_state curl_state = conf_curl_struct.curl_state; + workingState = conf_curl_struct.working_state; // ============== // do the request @@ -1103,7 +334,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat handle_curl_option_return_code( curl_easy_getinfo(curl_state.curl, CURLINFO_TOTAL_TIME, &total_time), "CURLINFO_TOTAL_TIME"); - size_t page_len = get_content_length(&curl_state.header_buf, &curl_state.body_buf); + size_t page_len = get_content_length(curl_state.header_buf, curl_state.body_buf); char perfstring[DEFAULT_BUFFER_SIZE]; if (config.show_extended_perfdata) { double time_connect; @@ -1144,12 +375,12 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } /* return a CRITICAL status if we couldn't read any data */ - if (strlen(curl_state.header_buf.buf) == 0 && strlen(curl_state.body_buf.buf) == 0) { + if (strlen(curl_state.header_buf->buf) == 0 && strlen(curl_state.body_buf->buf) == 0) { die(STATE_CRITICAL, _("HTTP CRITICAL - No header received from host\n")); } /* get status line of answer, check sanity of HTTP code */ - if (curlhelp_parse_statusline(curl_state.header_buf.buf, &curl_state.status_line) < 0) { + if (curlhelp_parse_statusline(curl_state.header_buf->buf, curl_state.status_line) < 0) { snprintf(msg, DEFAULT_BUFFER_SIZE, "Unparsable status line in %.3g seconds response time|%s\n", total_time, perfstring); @@ -1170,22 +401,22 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat /* print status line, header, body if verbose */ if (verbose >= 2) { - printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", curl_state.header_buf.buf, - (workingState.no_body ? " [[ skipped ]]" : curl_state.body_buf.buf)); + printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", curl_state.header_buf->buf, + (workingState.no_body ? " [[ skipped ]]" : curl_state.body_buf->buf)); } /* make sure the status line matches the response we are looking for */ - if (!expected_statuscode(curl_state.status_line.first_line, config.server_expect.string)) { + if (!expected_statuscode(curl_state.status_line->first_line, config.server_expect.string)) { if (workingState.serverPort == HTTP_PORT) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), - curl_state.status_line.first_line); + curl_state.status_line->first_line); } else { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), - workingState.serverPort, curl_state.status_line.first_line); + workingState.serverPort, curl_state.status_line->first_line); } die(STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg, config.show_body ? "\n" : "", - config.show_body ? curl_state.body_buf.buf : ""); + config.show_body ? curl_state.body_buf->buf : ""); } mp_state_enum result = STATE_OK; @@ -1200,7 +431,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat /* illegal return codes result in a critical state */ if (httpReturnCode >= 600 || httpReturnCode < 100) { die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), - curl_state.status_line.http_code, curl_state.status_line.msg); + curl_state.status_line->http_code, curl_state.status_line->msg); /* server errors result in a critical state */ } else if (httpReturnCode >= 500) { result = STATE_CRITICAL; @@ -1211,14 +442,14 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } else if (httpReturnCode >= 300) { if (config.on_redirect_dependent) { if (config.followmethod == FOLLOW_LIBCURL) { - httpReturnCode = curl_state.status_line.http_code; + httpReturnCode = curl_state.status_line->http_code; } else { /* old check_http style redirection, if we come * back here, we are in the same status as with * the libcurl method */ redir_wrapper redir_result = - redir(&curl_state.header_buf, config, redir_depth, workingState); + redir(curl_state.header_buf, config, redir_depth, workingState); cleanup(curl_state); check_http(config, redir_result.working_state, redir_result.redir_depth); } @@ -1252,21 +483,21 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } /* check status codes, set exit status accordingly */ - if (curl_state.status_line.http_code != httpReturnCode) { + if (curl_state.status_line->http_code != httpReturnCode) { die(STATE_CRITICAL, _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"), - string_statuscode(curl_state.status_line.http_major, curl_state.status_line.http_minor), - curl_state.status_line.http_code, curl_state.status_line.msg, httpReturnCode); + string_statuscode(curl_state.status_line->http_major, curl_state.status_line->http_minor), + curl_state.status_line->http_code, curl_state.status_line->msg, httpReturnCode); } if (config.maximum_age >= 0) { result = max_state_alt( - check_document_dates(&curl_state.header_buf, &msg, config.maximum_age), result); + check_document_dates(curl_state.header_buf, msg, config.maximum_age), result); } /* Page and Header content checks go here */ if (strlen(config.header_expect)) { - if (!strstr(curl_state.header_buf.buf, config.header_expect)) { + if (!strstr(curl_state.header_buf->buf, config.header_expect)) { char output_header_search[30] = ""; strncpy(&output_header_search[0], config.header_expect, sizeof(output_header_search)); @@ -1289,7 +520,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } if (strlen(config.string_expect)) { - if (!strstr(curl_state.body_buf.buf, config.string_expect)) { + if (!strstr(curl_state.body_buf->buf, config.string_expect)) { char output_string_search[30] = ""; strncpy(&output_string_search[0], config.string_expect, sizeof(output_string_search)); @@ -1313,7 +544,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat if (strlen(config.regexp)) { regmatch_t pmatch[REGS]; - int errcode = regexec(&config.compiled_regex, curl_state.body_buf.buf, REGS, pmatch, 0); + int errcode = regexec(&config.compiled_regex, curl_state.body_buf->buf, REGS, pmatch, 0); if ((errcode == 0 && !config.invert_regex) || (errcode == REG_NOMATCH && config.invert_regex)) { /* OK - No-op to avoid changing the logic around it */ @@ -1379,10 +610,10 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat die((int)max_state_alt(result, result_ssl), "HTTP %s: %s %d %s%s%s - %zu bytes in %.3f second response time %s|%s\n%s%s", state_text(result), - string_statuscode(curl_state.status_line.http_major, curl_state.status_line.http_minor), - curl_state.status_line.http_code, curl_state.status_line.msg, strlen(msg) > 0 ? " - " : "", + string_statuscode(curl_state.status_line->http_major, curl_state.status_line->http_minor), + curl_state.status_line->http_code, curl_state.status_line->msg, strlen(msg) > 0 ? " - " : "", msg, page_len, total_time, (config.display_html ? "" : ""), perfstring, - (config.show_body ? curl_state.body_buf.buf : ""), (config.show_body ? "\n" : "")); + (config.show_body ? curl_state.body_buf->buf : ""), (config.show_body ? "\n" : "")); return max_state_alt(result, result_ssl); } @@ -1574,14 +805,6 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config return result; } -/* check whether a file exists */ -void test_file(char *path) { - if (access(path, R_OK) == 0) { - return; - } - usage2(_("file does not exist or is not readable"), path); -} - check_curl_config_wrapper process_arguments(int argc, char **argv) { enum { INVERT_REGEX = CHAR_MAX + 1, @@ -2212,42 +1435,6 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { return result; } -char *perfd_time(double elapsed_time, thresholds *thlds, long socket_timeout) { - return fperfdata("time", elapsed_time, "s", (thlds->warning != NULL), - thlds->warning ? thlds->warning->end : 0, (thlds->critical != NULL), - thlds->critical ? thlds->critical->end : 0, true, 0, true, socket_timeout); -} - -char *perfd_time_connect(double elapsed_time_connect, long socket_timeout) { - return fperfdata("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, - socket_timeout); -} - -char *perfd_time_ssl(double elapsed_time_ssl, long socket_timeout) { - return fperfdata("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, - socket_timeout); -} - -char *perfd_time_headers(double elapsed_time_headers, long socket_timeout) { - return fperfdata("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, - socket_timeout); -} - -char *perfd_time_firstbyte(double elapsed_time_firstbyte, long socket_timeout) { - return fperfdata("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, - true, socket_timeout); -} - -char *perfd_time_transfer(double elapsed_time_transfer, long socket_timeout) { - return fperfdata("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, - true, socket_timeout); -} - -char *perfd_size(size_t page_len, int min_page_len) { - return perfdata("size", page_len, "B", (min_page_len > 0), min_page_len, (min_page_len > 0), 0, - true, 0, false, 0); -} - void print_help(void) { print_revision(progname, NP_VERSION); @@ -2523,403 +1710,6 @@ void print_usage(void) { void print_curl_version(void) { printf("%s\n", curl_version()); } -int curlhelp_initwritebuffer(curlhelp_write_curlbuf *buf) { - buf->bufsize = DEFAULT_BUFFER_SIZE; - buf->buflen = 0; - buf->buf = (char *)malloc(buf->bufsize); - if (buf->buf == NULL) { - return -1; - } - return 0; -} - -size_t curlhelp_buffer_write_callback(void *buffer, size_t size, size_t nmemb, void *stream) { - curlhelp_write_curlbuf *buf = (curlhelp_write_curlbuf *)stream; - - while (buf->bufsize < buf->buflen + size * nmemb + 1) { - buf->bufsize = buf->bufsize * 2; - buf->buf = (char *)realloc(buf->buf, buf->bufsize); - if (buf->buf == NULL) { - fprintf(stderr, "malloc failed (%d) %s\n", errno, strerror(errno)); - return 0; - } - } - - memcpy(buf->buf + buf->buflen, buffer, size * nmemb); - buf->buflen += size * nmemb; - buf->buf[buf->buflen] = '\0'; - - return size * nmemb; -} - -size_t curlhelp_buffer_read_callback(void *buffer, size_t size, size_t nmemb, void *stream) { - curlhelp_read_curlbuf *buf = (curlhelp_read_curlbuf *)stream; - - size_t minimalSize = min(nmemb * size, buf->buflen - buf->pos); - - memcpy(buffer, buf->buf + buf->pos, minimalSize); - buf->pos += minimalSize; - - return minimalSize; -} - -void curlhelp_freewritebuffer(curlhelp_write_curlbuf *buf) { - free(buf->buf); - buf->buf = NULL; -} - -int curlhelp_initreadbuffer(curlhelp_read_curlbuf *buf, const char *data, size_t datalen) { - buf->buflen = datalen; - buf->buf = (char *)malloc(buf->buflen); - if (buf->buf == NULL) { - return -1; - } - memcpy(buf->buf, data, datalen); - buf->pos = 0; - return 0; -} - -void curlhelp_freereadbuffer(curlhelp_read_curlbuf *buf) { - free(buf->buf); - buf->buf = NULL; -} - -/* TODO: where to put this, it's actually part of sstrings2 (logically)? - */ -const char *strrstr2(const char *haystack, const char *needle) { - if (haystack == NULL || needle == NULL) { - return NULL; - } - - if (haystack[0] == '\0' || needle[0] == '\0') { - return NULL; - } - - int counter = 0; - const char *prev_pos = NULL; - const char *pos = haystack; - size_t len = strlen(needle); - for (;;) { - pos = strstr(pos, needle); - if (pos == NULL) { - if (counter == 0) { - return NULL; - } - return prev_pos; - } - counter++; - prev_pos = pos; - pos += len; - if (*pos == '\0') { - return prev_pos; - } - } -} - -int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) { - /* find last start of a new header */ - const char *start = strrstr2(buf, "\r\nHTTP/"); - if (start != NULL) { - start += 2; - buf = start; - } - - char *first_line_end = strstr(buf, "\r\n"); - if (first_line_end == NULL) { - return -1; - } - - size_t first_line_len = (size_t)(first_line_end - buf); - status_line->first_line = (char *)malloc(first_line_len + 1); - if (status_line->first_line == NULL) { - return -1; - } - memcpy(status_line->first_line, buf, first_line_len); - status_line->first_line[first_line_len] = '\0'; - char *first_line_buf = strdup(status_line->first_line); - - /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */ - char *temp_string = strtok(first_line_buf, "/"); - if (temp_string == NULL) { - free(first_line_buf); - return -1; - } - if (strcmp(temp_string, "HTTP") != 0) { - free(first_line_buf); - return -1; - } - - temp_string = strtok(NULL, " "); - if (temp_string == NULL) { - free(first_line_buf); - return -1; - } - - char *temp_string_2; - if (strchr(temp_string, '.') != NULL) { - - /* HTTP 1.x case */ - strtok(temp_string, "."); - status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10); - if (*temp_string_2 != '\0') { - free(first_line_buf); - return -1; - } - strtok(NULL, " "); - status_line->http_minor = (int)strtol(temp_string, &temp_string_2, 10); - if (*temp_string_2 != '\0') { - free(first_line_buf); - return -1; - } - temp_string += 4; /* 1.x SP */ - } else { - /* HTTP 2 case */ - status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10); - status_line->http_minor = 0; - temp_string += 2; /* 2 SP */ - } - - /* status code: "404" or "404.1", then SP */ - temp_string = strtok(temp_string, " "); - if (temp_string == NULL) { - free(first_line_buf); - return -1; - } - if (strchr(temp_string, '.') != NULL) { - char *ppp; - ppp = strtok(temp_string, "."); - status_line->http_code = (int)strtol(ppp, &temp_string_2, 10); - if (*temp_string_2 != '\0') { - free(first_line_buf); - return -1; - } - ppp = strtok(NULL, ""); - status_line->http_subcode = (int)strtol(ppp, &temp_string_2, 10); - if (*temp_string_2 != '\0') { - free(first_line_buf); - return -1; - } - temp_string += 6; /* 400.1 SP */ - } else { - status_line->http_code = (int)strtol(temp_string, &temp_string_2, 10); - status_line->http_subcode = -1; - if (*temp_string_2 != '\0') { - free(first_line_buf); - return -1; - } - temp_string += 4; /* 400 SP */ - } - - /* Human readable message: "Not Found" CRLF */ - - temp_string = strtok(temp_string, ""); - if (temp_string == NULL) { - status_line->msg = ""; - return 0; - } - status_line->msg = status_line->first_line + (temp_string - first_line_buf); - free(first_line_buf); - - return 0; -} - -void curlhelp_free_statusline(curlhelp_statusline *status_line) { free(status_line->first_line); } - -char *get_header_value(const struct phr_header *headers, const size_t nof_headers, - const char *header) { - for (size_t i = 0; i < nof_headers; i++) { - if (headers[i].name != NULL && - strncasecmp(header, headers[i].name, max(headers[i].name_len, 4)) == 0) { - return strndup(headers[i].value, headers[i].value_len); - } - } - return NULL; -} - -mp_state_enum check_document_dates(const curlhelp_write_curlbuf *header_buf, - char (*msg)[DEFAULT_BUFFER_SIZE], int maximum_age) { - struct phr_header headers[255]; - size_t nof_headers = 255; - curlhelp_statusline status_line; - size_t msglen; - int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, - &status_line.http_minor, &status_line.http_code, &status_line.msg, - &msglen, headers, &nof_headers, 0); - - if (res == -1) { - die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); - } - - char *server_date = get_header_value(headers, nof_headers, "date"); - char *document_date = get_header_value(headers, nof_headers, "last-modified"); - - mp_state_enum date_result = STATE_OK; - if (!server_date || !*server_date) { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date unknown, "), *msg); - strcpy(*msg, tmp); - - date_result = max_state_alt(STATE_UNKNOWN, date_result); - - } else if (!document_date || !*document_date) { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument modification date unknown, "), *msg); - strcpy(*msg, tmp); - - date_result = max_state_alt(STATE_CRITICAL, date_result); - - } else { - time_t srv_data = curl_getdate(server_date, NULL); - time_t doc_data = curl_getdate(document_date, NULL); - if (verbose >= 2) { - printf("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data, - document_date, (int)doc_data); - } - if (srv_data <= 0) { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date \"%100s\" unparsable, "), *msg, - server_date); - strcpy(*msg, tmp); - - date_result = max_state_alt(STATE_CRITICAL, date_result); - } else if (doc_data <= 0) { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument date \"%100s\" unparsable, "), *msg, - document_date); - strcpy(*msg, tmp); - - date_result = max_state_alt(STATE_CRITICAL, date_result); - } else if (doc_data > srv_data + 30) { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument is %d seconds in the future, "), *msg, - (int)doc_data - (int)srv_data); - strcpy(*msg, tmp); - - date_result = max_state_alt(STATE_CRITICAL, date_result); - } else if (doc_data < srv_data - maximum_age) { - time_t last_modified = (srv_data - doc_data); - if (last_modified > (60 * 60 * 24 * 2)) { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), *msg, - ((float)last_modified) / (60 * 60 * 24)); - strcpy(*msg, tmp); - - date_result = max_state_alt(STATE_CRITICAL, date_result); - } else { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %ld:%02ld:%02ld ago, "), *msg, - last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60); - strcpy(*msg, tmp); - - date_result = max_state_alt(STATE_CRITICAL, date_result); - } - } - } - - if (server_date) { - free(server_date); - } - if (document_date) { - free(document_date); - } - - return date_result; -} - -size_t get_content_length(const curlhelp_write_curlbuf *header_buf, - const curlhelp_write_curlbuf *body_buf) { - struct phr_header headers[255]; - size_t nof_headers = 255; - size_t msglen; - curlhelp_statusline status_line; - int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, - &status_line.http_minor, &status_line.http_code, &status_line.msg, - &msglen, headers, &nof_headers, 0); - - if (res == -1) { - die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); - } - - char *content_length_s = get_header_value(headers, nof_headers, "content-length"); - if (!content_length_s) { - return header_buf->buflen + body_buf->buflen; - } - - content_length_s += strspn(content_length_s, " \t"); - size_t content_length = atoi(content_length_s); - if (content_length != body_buf->buflen) { - /* TODO: should we warn if the actual and the reported body length don't match? */ - } - - if (content_length_s) { - free(content_length_s); - } - - return header_buf->buflen + body_buf->buflen; -} - -/* TODO: is there a better way in libcurl to check for the SSL library? */ -curlhelp_ssl_library curlhelp_get_ssl_library(void) { - curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN; - - curl_version_info_data *version_data = curl_version_info(CURLVERSION_NOW); - if (version_data == NULL) { - return CURLHELP_SSL_LIBRARY_UNKNOWN; - } - - char *ssl_version = strdup(version_data->ssl_version); - if (ssl_version == NULL) { - return CURLHELP_SSL_LIBRARY_UNKNOWN; - } - - char *library = strtok(ssl_version, "/"); - if (library == NULL) { - return CURLHELP_SSL_LIBRARY_UNKNOWN; - } - - if (strcmp(library, "OpenSSL") == 0) { - ssl_library = CURLHELP_SSL_LIBRARY_OPENSSL; - } else if (strcmp(library, "LibreSSL") == 0) { - ssl_library = CURLHELP_SSL_LIBRARY_LIBRESSL; - } else if (strcmp(library, "GnuTLS") == 0) { - ssl_library = CURLHELP_SSL_LIBRARY_GNUTLS; - } else if (strcmp(library, "NSS") == 0) { - ssl_library = CURLHELP_SSL_LIBRARY_NSS; - } - - if (verbose >= 2) { - printf("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library, - ssl_library); - } - - free(ssl_version); - - return ssl_library; -} - -const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library ssl_library) { - switch (ssl_library) { - case CURLHELP_SSL_LIBRARY_OPENSSL: - return "OpenSSL"; - case CURLHELP_SSL_LIBRARY_LIBRESSL: - return "LibreSSL"; - case CURLHELP_SSL_LIBRARY_GNUTLS: - return "GnuTLS"; - case CURLHELP_SSL_LIBRARY_NSS: - return "NSS"; - case CURLHELP_SSL_LIBRARY_UNKNOWN: - default: - return "unknown"; - } -} - #ifdef LIBCURL_FEATURE_SSL # ifndef USE_OPENSSL time_t parse_cert_date(const char *s) { @@ -2938,35 +1728,31 @@ time_t parse_cert_date(const char *s) { return date; } +# endif /* USE_OPENSSL */ +#endif /* LIBCURL_FEATURE_SSL */ +#ifdef LIBCURL_FEATURE_SSL +# ifndef USE_OPENSSL /* TODO: this needs cleanup in the sslutils.c, maybe we the #else case to * OpenSSL could be this function */ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_warn, int days_till_exp_crit) { - struct curl_slist *slist; - int cname_found = 0; - char *start_date_str = NULL; - char *end_date_str = NULL; - time_t start_date; - time_t end_date; - char *tz; - float time_left; - int days_left; - int time_remaining; - char timestamp[50] = ""; - int status = STATE_UNKNOWN; if (verbose >= 2) { printf("**** REQUEST CERTIFICATES ****\n"); } + char *start_date_str = NULL; + char *end_date_str = NULL; bool have_first_cert = false; + bool cname_found = false; for (int i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) { if (have_first_cert) { break; } + struct curl_slist *slist; for (slist = cert_ptr->to_certinfo->certinfo[i]; slist; slist = slist->next) { /* find first common name in subject, * TODO: check alternative subjects for @@ -2982,7 +1768,7 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_ } if (p != NULL) { if (strncmp(host_name, p + d, strlen(host_name)) == 0) { - cname_found = 1; + cname_found = true; } } } else if (strncasecmp(slist->data, "Start Date:", 11) == 0) { @@ -3008,7 +1794,7 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_ return STATE_CRITICAL; } - start_date = parse_cert_date(start_date_str); + time_t start_date = parse_cert_date(start_date_str); if (start_date <= 0) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Start Date' in certificate: '%s'"), start_date_str); @@ -3016,7 +1802,7 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_ return STATE_WARNING; } - end_date = parse_cert_date(end_date_str); + time_t end_date = parse_cert_date(end_date_str); if (end_date <= 0) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Expire Date' in certificate: '%s'"), start_date_str); @@ -3024,11 +1810,13 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_ return STATE_WARNING; } - time_left = difftime(end_date, time(NULL)); - days_left = time_left / 86400; - tz = getenv("TZ"); + float time_left = difftime(end_date, time(NULL)); + int days_left = time_left / 86400; + char *tz = getenv("TZ"); setenv("TZ", "GMT", 1); tzset(); + + char timestamp[50] = ""; strftime(timestamp, 50, "%c %z", localtime(&end_date)); if (tz) { setenv("TZ", tz, 1); @@ -3037,6 +1825,8 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_ } tzset(); + mp_state_enum status = STATE_UNKNOWN; + int time_remaining; if (days_left > 0 && days_left <= days_till_exp_warn) { printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, days_left, diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c new file mode 100644 index 00000000..42d63bc4 --- /dev/null +++ b/plugins/check_curl.d/check_curl_helpers.c @@ -0,0 +1,1217 @@ +#include "./check_curl_helpers.h" +#include +#include +#include +#include +#include +#include "../utils.h" + +extern int verbose; +char msg[DEFAULT_BUFFER_SIZE]; +char errbuf[MAX_INPUT_BUFFER]; +bool is_openssl_callback = false; +bool add_sslctx_verify_fun = false; + +check_curl_configure_curl_wrapper +check_curl_configure_curl(const check_curl_static_curl_config config, + check_curl_working_state working_state, bool check_cert, + bool on_redirect_dependent, int follow_method, int max_depth) { + check_curl_configure_curl_wrapper result = { + .errorcode = OK, + .curl_state = + { + .curl_global_initialized = false, + .curl_easy_initialized = false, + .curl = NULL, + + .body_buf_initialized = false, + .body_buf = NULL, + .header_buf_initialized = false, + .header_buf = NULL, + .status_line_initialized = false, + .status_line = NULL, + .put_buf_initialized = false, + .put_buf = NULL, + + .header_list = NULL, + .host = NULL, + }, + }; + + if ((result.curl_state.status_line = calloc(1, sizeof(curlhelp_statusline))) == NULL) { + die(STATE_UNKNOWN, "HTTP UNKNOWN - allocation of statusline failed\n"); + } + + if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { + die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n"); + } + result.curl_state.curl_global_initialized = true; + + if ((result.curl_state.curl = curl_easy_init()) == NULL) { + die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n"); + } + result.curl_state.curl_easy_initialized = true; + + if (verbose >= 1) { + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_VERBOSE, 1), + "CURLOPT_VERBOSE"); + } + + /* print everything on stdout like check_http would do */ + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_STDERR, stdout), + "CURLOPT_STDERR"); + + if (config.automatic_decompression) { +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_ACCEPT_ENCODING, ""), + "CURLOPT_ACCEPT_ENCODING"); +#else + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING"); +#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */ + } + + /* initialize buffer for body of the answer */ + if (curlhelp_initwritebuffer(&result.curl_state.body_buf) < 0) { + die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n"); + } + result.curl_state.body_buf_initialized = true; + + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEFUNCTION, + curlhelp_buffer_write_callback), + "CURLOPT_WRITEFUNCTION"); + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEDATA, + (void *)result.curl_state.body_buf), + "CURLOPT_WRITEDATA"); + + /* initialize buffer for header of the answer */ + if (curlhelp_initwritebuffer(&result.curl_state.header_buf) < 0) { + die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n"); + } + result.curl_state.header_buf_initialized = true; + + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_HEADERFUNCTION, + curlhelp_buffer_write_callback), + "CURLOPT_HEADERFUNCTION"); + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEHEADER, + (void *)result.curl_state.header_buf), + "CURLOPT_WRITEHEADER"); + + /* set the error buffer */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_ERRORBUFFER, errbuf), + "CURLOPT_ERRORBUFFER"); + + /* set timeouts */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_CONNECTTIMEOUT, config.socket_timeout), + "CURLOPT_CONNECTTIMEOUT"); + + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_TIMEOUT, config.socket_timeout), + "CURLOPT_TIMEOUT"); + + /* enable haproxy protocol */ + if (config.haproxy_protocol) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_HAPROXYPROTOCOL, 1L), + "CURLOPT_HAPROXYPROTOCOL"); + } + + // fill dns resolve cache to make curl connect to the given server_address instead of the + // host_name, only required for ssl, because we use the host_name later on to make SNI happy + char dnscache[DEFAULT_BUFFER_SIZE]; + char addrstr[DEFAULT_BUFFER_SIZE / 2]; + if (working_state.use_ssl && working_state.host_name != NULL) { + int res; + if ((res = lookup_host(working_state.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2, + config.sin_family)) != 0) { + snprintf(msg, DEFAULT_BUFFER_SIZE, + _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), + working_state.server_address, res, gai_strerror(res)); + die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); + } + snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", working_state.host_name, + working_state.serverPort, addrstr); + result.curl_state.host = curl_slist_append(NULL, dnscache); + curl_easy_setopt(result.curl_state.curl, CURLOPT_RESOLVE, result.curl_state.host); + if (verbose >= 1) { + printf("* curl CURLOPT_RESOLVE: %s\n", dnscache); + } + } + + // If server_address is an IPv6 address it must be surround by square brackets + struct in6_addr tmp_in_addr; + if (inet_pton(AF_INET6, working_state.server_address, &tmp_in_addr) == 1) { + char *new_server_address = calloc(strlen(working_state.server_address) + 3, sizeof(char)); + if (new_server_address == NULL) { + die(STATE_UNKNOWN, "HTTP UNKNOWN - Unable to allocate memory\n"); + } + snprintf(new_server_address, strlen(working_state.server_address) + 3, "[%s]", + working_state.server_address); + working_state.server_address = new_server_address; + } + + /* compose URL: use the address we want to connect to, set Host: header later */ + char url[DEFAULT_BUFFER_SIZE]; + snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", working_state.use_ssl ? "https" : "http", + (working_state.use_ssl & (working_state.host_name != NULL)) + ? working_state.host_name + : working_state.server_address, + working_state.serverPort, working_state.server_url); + + if (verbose >= 1) { + printf("* curl CURLOPT_URL: %s\n", url); + } + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, url), + "CURLOPT_URL"); + + /* extract proxy information for legacy proxy https requests */ + if (!strcmp(working_state.http_method, "CONNECT") || + strstr(working_state.server_url, "http") == working_state.server_url) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXY, working_state.server_address), + "CURLOPT_PROXY"); + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXYPORT, + (long)working_state.serverPort), + "CURLOPT_PROXYPORT"); + if (verbose >= 2) { + printf("* curl CURLOPT_PROXY: %s:%d\n", working_state.server_address, + working_state.serverPort); + } + working_state.http_method = "GET"; + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, working_state.server_url), + "CURLOPT_URL"); + } + + /* disable body for HEAD request */ + if (working_state.http_method && !strcmp(working_state.http_method, "HEAD")) { + working_state.no_body = true; + } + + /* set HTTP protocol version */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_HTTP_VERSION, config.curl_http_version), + "CURLOPT_HTTP_VERSION"); + + /* set HTTP method */ + if (working_state.http_method) { + if (!strcmp(working_state.http_method, "POST")) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_POST, 1), "CURLOPT_POST"); + } else if (!strcmp(working_state.http_method, "PUT")) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD"); + } else { + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, + CURLOPT_CUSTOMREQUEST, + working_state.http_method), + "CURLOPT_CUSTOMREQUEST"); + } + } + + char *force_host_header = NULL; + /* check if Host header is explicitly set in options */ + if (config.http_opt_headers_count) { + for (size_t i = 0; i < config.http_opt_headers_count; i++) { + if (strncmp(config.http_opt_headers[i], "Host:", 5) == 0) { + force_host_header = config.http_opt_headers[i]; + } + } + } + + /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in + * anyway */ + char http_header[DEFAULT_BUFFER_SIZE]; + if (working_state.host_name != NULL && force_host_header == NULL) { + if ((working_state.virtualPort != HTTP_PORT && !working_state.use_ssl) || + (working_state.virtualPort != HTTPS_PORT && working_state.use_ssl)) { + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", working_state.host_name, + working_state.virtualPort); + } else { + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", working_state.host_name); + } + result.curl_state.header_list = + curl_slist_append(result.curl_state.header_list, http_header); + } + + /* always close connection, be nice to servers */ + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Connection: close"); + result.curl_state.header_list = curl_slist_append(result.curl_state.header_list, http_header); + + /* attach additional headers supplied by the user */ + /* optionally send any other header tag */ + if (config.http_opt_headers_count) { + for (size_t i = 0; i < config.http_opt_headers_count; i++) { + result.curl_state.header_list = + curl_slist_append(result.curl_state.header_list, config.http_opt_headers[i]); + } + } + + /* set HTTP headers */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_HTTPHEADER, result.curl_state.header_list), + "CURLOPT_HTTPHEADER"); + +#ifdef LIBCURL_FEATURE_SSL + /* set SSL version, warn about insecure or unsupported versions */ + if (working_state.use_ssl) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLVERSION, config.ssl_version), + "CURLOPT_SSLVERSION"); + } + + /* client certificate and key to present to server (SSL) */ + if (config.client_cert) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLCERT, config.client_cert), + "CURLOPT_SSLCERT"); + } + + if (config.client_privkey) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLKEY, config.client_privkey), + "CURLOPT_SSLKEY"); + } + + if (config.ca_cert) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_CAINFO, config.ca_cert), + "CURLOPT_CAINFO"); + } + + if (config.ca_cert || config.verify_peer_and_host) { + /* per default if we have a CA verify both the peer and the + * hostname in the certificate, can be switched off later */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 1), + "CURLOPT_SSL_VERIFYPEER"); + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 2), + "CURLOPT_SSL_VERIFYHOST"); + } else { + /* backward-compatible behaviour, be tolerant in checks + * TODO: depending on more options have aspects we want + * to be less tolerant about ssl verfications + */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 0), + "CURLOPT_SSL_VERIFYPEER"); + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 0), + "CURLOPT_SSL_VERIFYHOST"); + } + + /* detect SSL library used by libcurl */ + curlhelp_ssl_library ssl_library = curlhelp_get_ssl_library(); + + /* try hard to get a stack of certificates to verify against */ + if (check_cert) { +# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) + /* inform curl to report back certificates */ + switch (ssl_library) { + case CURLHELP_SSL_LIBRARY_OPENSSL: + case CURLHELP_SSL_LIBRARY_LIBRESSL: + /* set callback to extract certificate with OpenSSL context function (works with + * OpenSSL-style libraries only!) */ +# ifdef USE_OPENSSL + /* libcurl and monitoring plugins built with OpenSSL, good */ + add_sslctx_verify_fun = true; + is_openssl_callback = true; +# endif /* USE_OPENSSL */ + /* libcurl is built with OpenSSL, monitoring plugins, so falling + * back to manually extracting certificate information */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); + break; + + case CURLHELP_SSL_LIBRARY_NSS: +# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) + /* NSS: support for CERTINFO is implemented since 7.34.0 */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); +# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ + die(STATE_CRITICAL, + "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library " + "'%s' is too old)\n", + curlhelp_get_ssl_library_string(ssl_library)); +# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ + break; + + case CURLHELP_SSL_LIBRARY_GNUTLS: +# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) + /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); +# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */ + die(STATE_CRITICAL, + "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library " + "'%s' is too old)\n", + curlhelp_get_ssl_library_string(ssl_library)); +# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */ + break; + + case CURLHELP_SSL_LIBRARY_UNKNOWN: + default: + die(STATE_CRITICAL, + "HTTP CRITICAL - Cannot retrieve certificates (unknown SSL library '%s', must " + "implement first)\n", + curlhelp_get_ssl_library_string(ssl_library)); + break; + } +# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ + /* old libcurl, our only hope is OpenSSL, otherwise we are out of luck */ + if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL || + ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL) { + add_sslctx_verify_fun = true; + } else { + die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no " + "CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl " + "too old and has no CURLOPT_CERTINFO)\n"); + } +# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ + } + +# if LIBCURL_VERSION_NUM >= \ + MAKE_LIBCURL_VERSION(7, 10, 6) /* required for CURLOPT_SSL_CTX_FUNCTION */ + // ssl ctx function is not available with all ssl backends + if (curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_CTX_FUNCTION, NULL) != + CURLE_UNKNOWN_OPTION) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), + "CURLOPT_SSL_CTX_FUNCTION"); + } +# endif +#endif /* LIBCURL_FEATURE_SSL */ + + /* set default or user-given user agent identification */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_USERAGENT, config.user_agent), + "CURLOPT_USERAGENT"); + + /* proxy-authentication */ + if (strcmp(config.proxy_auth, "")) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXYUSERPWD, config.proxy_auth), + "CURLOPT_PROXYUSERPWD"); + } + + /* authentication */ + if (strcmp(config.user_auth, "")) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_USERPWD, config.user_auth), + "CURLOPT_USERPWD"); + } + /* TODO: parameter auth method, bitfield of following methods: + * CURLAUTH_BASIC (default) + * CURLAUTH_DIGEST + * CURLAUTH_DIGEST_IE + * CURLAUTH_NEGOTIATE + * CURLAUTH_NTLM + * CURLAUTH_NTLM_WB + * + * convenience tokens for typical sets of methods: + * CURLAUTH_ANYSAFE: most secure, without BASIC + * or CURLAUTH_ANY: most secure, even BASIC if necessary + * + * handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPAUTH, + * (long)CURLAUTH_DIGEST ), "CURLOPT_HTTPAUTH"); + */ + + /* handle redirections */ + if (on_redirect_dependent) { + if (follow_method == FOLLOW_LIBCURL) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_FOLLOWLOCATION, 1), + "CURLOPT_FOLLOWLOCATION"); + + /* default -1 is infinite, not good, could lead to zombie plugins! + Setting it to one bigger than maximal limit to handle errors nicely below + */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_MAXREDIRS, max_depth + 1), + "CURLOPT_MAXREDIRS"); + + /* for now allow only http and https (we are a http(s) check plugin in the end) */ +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 85, 0) + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"), + "CURLOPT_REDIR_PROTOCOLS_STR"); +#elif LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4) + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, + CURLOPT_REDIR_PROTOCOLS, + CURLPROTO_HTTP | CURLPROTO_HTTPS), + "CURLOPT_REDIRECT_PROTOCOLS"); +#endif + + /* TODO: handle the following aspects of redirection, make them + * command line options too later: + CURLOPT_POSTREDIR: method switch + CURLINFO_REDIRECT_URL: custom redirect option + CURLOPT_REDIRECT_PROTOCOLS: allow people to step outside safe protocols + CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range + option here is nice like for expected page size? + */ + } else { + /* old style redirection*/ + } + } + /* no-body */ + if (working_state.no_body) { + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_NOBODY, 1), + "CURLOPT_NOBODY"); + } + + /* IPv4 or IPv6 forced DNS resolution */ + if (config.sin_family == AF_UNSPEC) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), + "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)"); + } else if (config.sin_family == AF_INET) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), + "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)"); + } +#if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) + else if (config.sin_family == AF_INET6) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), + "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)"); + } +#endif + + /* either send http POST data (any data, not only POST)*/ + if (!strcmp(working_state.http_method, "POST") || !strcmp(working_state.http_method, "PUT")) { + /* set content of payload for POST and PUT */ + if (config.http_content_type) { + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", + config.http_content_type); + result.curl_state.header_list = + curl_slist_append(result.curl_state.header_list, http_header); + } + /* NULL indicates "HTTP Continue" in libcurl, provide an empty string + * in case of no POST/PUT data */ + if (!working_state.http_post_data) { + working_state.http_post_data = ""; + } + + if (!strcmp(working_state.http_method, "POST")) { + /* POST method, set payload with CURLOPT_POSTFIELDS */ + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, + CURLOPT_POSTFIELDS, + working_state.http_post_data), + "CURLOPT_POSTFIELDS"); + } else if (!strcmp(working_state.http_method, "PUT")) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_READFUNCTION, + (curl_read_callback)curlhelp_buffer_read_callback), + "CURLOPT_READFUNCTION"); + if (curlhelp_initreadbuffer(&result.curl_state.put_buf, working_state.http_post_data, + strlen(working_state.http_post_data)) < 0) { + die(STATE_UNKNOWN, + "HTTP CRITICAL - out of memory allocating read buffer for PUT\n"); + } + result.curl_state.put_buf_initialized = true; + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, + CURLOPT_READDATA, + (void *)&result.curl_state.put_buf), + "CURLOPT_READDATA"); + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_INFILESIZE, + (curl_off_t)strlen(working_state.http_post_data)), + "CURLOPT_INFILESIZE"); + } + } + + /* cookie handling */ + if (config.cookie_jar_file != NULL) { + /* enable reading cookies from a file, and if the filename is an empty string, only + * enable the curl cookie engine */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_COOKIEFILE, config.cookie_jar_file), + "CURLOPT_COOKIEFILE"); + /* now enable saving cookies to a file, but only if the filename is not an empty string, + * since writing it would fail */ + if (*config.cookie_jar_file) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_COOKIEJAR, config.cookie_jar_file), + "CURLOPT_COOKIEJAR"); + } + } + + result.working_state = working_state; + + return result; +} + +void handle_curl_option_return_code(CURLcode res, const char *option) { + if (res != CURLE_OK) { + snprintf(msg, DEFAULT_BUFFER_SIZE, + _("Error while setting cURL option '%s': cURL returned %d - %s"), option, res, + curl_easy_strerror(res)); + die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); + } +} + +char *get_header_value(const struct phr_header *headers, const size_t nof_headers, + const char *header) { + for (size_t i = 0; i < nof_headers; i++) { + if (headers[i].name != NULL && + strncasecmp(header, headers[i].name, max(headers[i].name_len, 4)) == 0) { + return strndup(headers[i].value, headers[i].value_len); + } + } + return NULL; +} + +check_curl_working_state check_curl_working_state_init() { + check_curl_working_state result = { + .server_address = NULL, + .server_url = DEFAULT_SERVER_URL, + .host_name = NULL, + .http_method = NULL, + .http_post_data = NULL, + .virtualPort = 0, + .serverPort = HTTP_PORT, + .use_ssl = false, + .no_body = false, + }; + return result; +} + +check_curl_config check_curl_config_init() { + check_curl_config tmp = { + .initial_config = check_curl_working_state_init(), + + .curl_config = + { + .automatic_decompression = false, + .socket_timeout = DEFAULT_SOCKET_TIMEOUT, + .haproxy_protocol = false, + .sin_family = AF_UNSPEC, + .curl_http_version = CURL_HTTP_VERSION_NONE, + .http_opt_headers = NULL, + .http_opt_headers_count = 0, + .ssl_version = CURL_SSLVERSION_DEFAULT, + .client_cert = NULL, + .client_privkey = NULL, + .ca_cert = NULL, + .verify_peer_and_host = false, + .user_agent = {'\0'}, + .proxy_auth = "", + .user_auth = "", + .http_content_type = NULL, + .cookie_jar_file = NULL, + }, + .max_depth = DEFAULT_MAX_REDIRS, + .followmethod = FOLLOW_HTTP_CURL, + .followsticky = STICKY_NONE, + + .maximum_age = -1, + .regexp = {}, + .compiled_regex = {}, + .state_regex = STATE_CRITICAL, + .invert_regex = false, + .check_cert = false, + .continue_after_check_cert = false, + .days_till_exp_warn = 0, + .days_till_exp_crit = 0, + .thlds = NULL, + .min_page_len = 0, + .max_page_len = 0, + .server_expect = + { + .string = HTTP_EXPECT, + .is_present = false, + }, + .string_expect = "", + .header_expect = "", + .on_redirect_result_state = STATE_OK, + .on_redirect_dependent = true, + + .show_extended_perfdata = false, + .show_body = false, + .display_html = false, + }; + + snprintf(tmp.curl_config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", + "check_curl", NP_VERSION, VERSION, curl_version()); + + return tmp; +} + +/* TODO: is there a better way in libcurl to check for the SSL library? */ +curlhelp_ssl_library curlhelp_get_ssl_library(void) { + curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN; + + curl_version_info_data *version_data = curl_version_info(CURLVERSION_NOW); + if (version_data == NULL) { + return CURLHELP_SSL_LIBRARY_UNKNOWN; + } + + char *ssl_version = strdup(version_data->ssl_version); + if (ssl_version == NULL) { + return CURLHELP_SSL_LIBRARY_UNKNOWN; + } + + char *library = strtok(ssl_version, "/"); + if (library == NULL) { + return CURLHELP_SSL_LIBRARY_UNKNOWN; + } + + if (strcmp(library, "OpenSSL") == 0) { + ssl_library = CURLHELP_SSL_LIBRARY_OPENSSL; + } else if (strcmp(library, "LibreSSL") == 0) { + ssl_library = CURLHELP_SSL_LIBRARY_LIBRESSL; + } else if (strcmp(library, "GnuTLS") == 0) { + ssl_library = CURLHELP_SSL_LIBRARY_GNUTLS; + } else if (strcmp(library, "NSS") == 0) { + ssl_library = CURLHELP_SSL_LIBRARY_NSS; + } + + if (verbose >= 2) { + printf("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library, + ssl_library); + } + + free(ssl_version); + + return ssl_library; +} + +const char *curlhelp_get_ssl_library_string(const curlhelp_ssl_library ssl_library) { + switch (ssl_library) { + case CURLHELP_SSL_LIBRARY_OPENSSL: + return "OpenSSL"; + case CURLHELP_SSL_LIBRARY_LIBRESSL: + return "LibreSSL"; + case CURLHELP_SSL_LIBRARY_GNUTLS: + return "GnuTLS"; + case CURLHELP_SSL_LIBRARY_NSS: + return "NSS"; + case CURLHELP_SSL_LIBRARY_UNKNOWN: + default: + return "unknown"; + } +} + +size_t get_content_length(const curlhelp_write_curlbuf *header_buf, + const curlhelp_write_curlbuf *body_buf) { + struct phr_header headers[255]; + size_t nof_headers = 255; + size_t msglen; + curlhelp_statusline status_line; + int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, + &status_line.http_minor, &status_line.http_code, &status_line.msg, + &msglen, headers, &nof_headers, 0); + + if (res == -1) { + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); + } + + char *content_length_s = get_header_value(headers, nof_headers, "content-length"); + if (!content_length_s) { + return header_buf->buflen + body_buf->buflen; + } + + content_length_s += strspn(content_length_s, " \t"); + size_t content_length = atoi(content_length_s); + if (content_length != body_buf->buflen) { + /* TODO: should we warn if the actual and the reported body length don't match? */ + } + + if (content_length_s) { + free(content_length_s); + } + + return header_buf->buflen + body_buf->buflen; +} + +mp_state_enum check_document_dates(const curlhelp_write_curlbuf *header_buf, + const char msg[static DEFAULT_BUFFER_SIZE], + const int maximum_age) { + struct phr_header headers[255]; + size_t nof_headers = 255; + curlhelp_statusline status_line; + size_t msglen; + int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, + &status_line.http_minor, &status_line.http_code, &status_line.msg, + &msglen, headers, &nof_headers, 0); + + if (res == -1) { + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); + } + + char *server_date = get_header_value(headers, nof_headers, "date"); + char *document_date = get_header_value(headers, nof_headers, "last-modified"); + + mp_state_enum date_result = STATE_OK; + if (!server_date || !*server_date) { + char tmp[DEFAULT_BUFFER_SIZE]; + + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date unknown, "), msg); + strcpy(msg, tmp); + + date_result = max_state_alt(STATE_UNKNOWN, date_result); + + } else if (!document_date || !*document_date) { + char tmp[DEFAULT_BUFFER_SIZE]; + + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument modification date unknown, "), msg); + strcpy(msg, tmp); + + date_result = max_state_alt(STATE_CRITICAL, date_result); + + } else { + time_t srv_data = curl_getdate(server_date, NULL); + time_t doc_data = curl_getdate(document_date, NULL); + if (verbose >= 2) { + printf("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data, + document_date, (int)doc_data); + } + if (srv_data <= 0) { + char tmp[DEFAULT_BUFFER_SIZE]; + + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date \"%100s\" unparsable, "), msg, + server_date); + strcpy(msg, tmp); + + date_result = max_state_alt(STATE_CRITICAL, date_result); + } else if (doc_data <= 0) { + char tmp[DEFAULT_BUFFER_SIZE]; + + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument date \"%100s\" unparsable, "), msg, + document_date); + strcpy(msg, tmp); + + date_result = max_state_alt(STATE_CRITICAL, date_result); + } else if (doc_data > srv_data + 30) { + char tmp[DEFAULT_BUFFER_SIZE]; + + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument is %d seconds in the future, "), msg, + (int)doc_data - (int)srv_data); + strcpy(msg, tmp); + + date_result = max_state_alt(STATE_CRITICAL, date_result); + } else if (doc_data < srv_data - maximum_age) { + time_t last_modified = (srv_data - doc_data); + if (last_modified > (60 * 60 * 24 * 2)) { + char tmp[DEFAULT_BUFFER_SIZE]; + + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), msg, + ((float)last_modified) / (60 * 60 * 24)); + strcpy(msg, tmp); + + date_result = max_state_alt(STATE_CRITICAL, date_result); + } else { + char tmp[DEFAULT_BUFFER_SIZE]; + + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %ld:%02ld:%02ld ago, "), msg, + last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60); + strcpy(msg, tmp); + + date_result = max_state_alt(STATE_CRITICAL, date_result); + } + } + } + + if (server_date) { + free(server_date); + } + if (document_date) { + free(document_date); + } + + return date_result; +} + +void curlhelp_free_statusline(curlhelp_statusline *status_line) { free(status_line->first_line); } + +int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) { + /* find last start of a new header */ + const char *start = strrstr2(buf, "\r\nHTTP/"); + if (start != NULL) { + start += 2; + buf = start; + } + + char *first_line_end = strstr(buf, "\r\n"); + if (first_line_end == NULL) { + return -1; + } + + size_t first_line_len = (size_t)(first_line_end - buf); + status_line->first_line = (char *)calloc(first_line_len + 1, sizeof(char)); + if (status_line->first_line == NULL) { + return -1; + } + memcpy(status_line->first_line, buf, first_line_len); + status_line->first_line[first_line_len] = '\0'; + char *first_line_buf = strdup(status_line->first_line); + + /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */ + char *temp_string = strtok(first_line_buf, "/"); + if (temp_string == NULL) { + free(first_line_buf); + return -1; + } + if (strcmp(temp_string, "HTTP") != 0) { + free(first_line_buf); + return -1; + } + + temp_string = strtok(NULL, " "); + if (temp_string == NULL) { + free(first_line_buf); + return -1; + } + + char *temp_string_2; + if (strchr(temp_string, '.') != NULL) { + + /* HTTP 1.x case */ + strtok(temp_string, "."); + status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10); + if (*temp_string_2 != '\0') { + free(first_line_buf); + return -1; + } + strtok(NULL, " "); + status_line->http_minor = (int)strtol(temp_string, &temp_string_2, 10); + if (*temp_string_2 != '\0') { + free(first_line_buf); + return -1; + } + temp_string += 4; /* 1.x SP */ + } else { + /* HTTP 2 case */ + status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10); + status_line->http_minor = 0; + temp_string += 2; /* 2 SP */ + } + + /* status code: "404" or "404.1", then SP */ + temp_string = strtok(temp_string, " "); + if (temp_string == NULL) { + free(first_line_buf); + return -1; + } + if (strchr(temp_string, '.') != NULL) { + char *ppp; + ppp = strtok(temp_string, "."); + status_line->http_code = (int)strtol(ppp, &temp_string_2, 10); + if (*temp_string_2 != '\0') { + free(first_line_buf); + return -1; + } + ppp = strtok(NULL, ""); + status_line->http_subcode = (int)strtol(ppp, &temp_string_2, 10); + if (*temp_string_2 != '\0') { + free(first_line_buf); + return -1; + } + temp_string += 6; /* 400.1 SP */ + } else { + status_line->http_code = (int)strtol(temp_string, &temp_string_2, 10); + status_line->http_subcode = -1; + if (*temp_string_2 != '\0') { + free(first_line_buf); + return -1; + } + temp_string += 4; /* 400 SP */ + } + + /* Human readable message: "Not Found" CRLF */ + + temp_string = strtok(temp_string, ""); + if (temp_string == NULL) { + status_line->msg = ""; + return 0; + } + status_line->msg = status_line->first_line + (temp_string - first_line_buf); + free(first_line_buf); + + return 0; +} + +/* TODO: where to put this, it's actually part of sstrings2 (logically)? + */ +const char *strrstr2(const char *haystack, const char *needle) { + if (haystack == NULL || needle == NULL) { + return NULL; + } + + if (haystack[0] == '\0' || needle[0] == '\0') { + return NULL; + } + + int counter = 0; + const char *prev_pos = NULL; + const char *pos = haystack; + size_t len = strlen(needle); + for (;;) { + pos = strstr(pos, needle); + if (pos == NULL) { + if (counter == 0) { + return NULL; + } + return prev_pos; + } + counter++; + prev_pos = pos; + pos += len; + if (*pos == '\0') { + return prev_pos; + } + } +} + +void curlhelp_freereadbuffer(curlhelp_read_curlbuf *buf) { + free(buf->buf); + buf->buf = NULL; +} + +void curlhelp_freewritebuffer(curlhelp_write_curlbuf *buf) { + free(buf->buf); + buf->buf = NULL; +} + +int curlhelp_initreadbuffer(curlhelp_read_curlbuf **buf, const char *data, size_t datalen) { + if ((*buf = calloc(1, sizeof(curlhelp_read_curlbuf))) == NULL) { + return 1; + } + + (*buf)->buflen = datalen; + (*buf)->buf = (char *)calloc((*buf)->buflen, sizeof(char)); + if ((*buf)->buf == NULL) { + return -1; + } + memcpy((*buf)->buf, data, datalen); + (*buf)->pos = 0; + return 0; +} + +size_t curlhelp_buffer_read_callback(void *buffer, size_t size, size_t nmemb, void *stream) { + curlhelp_read_curlbuf *buf = (curlhelp_read_curlbuf *)stream; + + size_t minimalSize = min(nmemb * size, buf->buflen - buf->pos); + + memcpy(buffer, buf->buf + buf->pos, minimalSize); + buf->pos += minimalSize; + + return minimalSize; +} + +int curlhelp_initwritebuffer(curlhelp_write_curlbuf **buf) { + if ((*buf = calloc(1, sizeof(curlhelp_write_curlbuf))) == NULL) { + return 1; + } + (*buf)->bufsize = DEFAULT_BUFFER_SIZE * sizeof(char); + (*buf)->buflen = 0; + (*buf)->buf = (char *)calloc((*buf)->bufsize, sizeof(char)); + if ((*buf)->buf == NULL) { + return -1; + } + return 0; +} + +size_t curlhelp_buffer_write_callback(void *buffer, size_t size, size_t nmemb, void *stream) { + curlhelp_write_curlbuf *buf = (curlhelp_write_curlbuf *)stream; + + while (buf->bufsize < buf->buflen + size * nmemb + 1) { + buf->bufsize = buf->bufsize * 2; + buf->buf = (char *)realloc(buf->buf, buf->bufsize); + if (buf->buf == NULL) { + fprintf(stderr, "malloc failed (%d) %s\n", errno, strerror(errno)); + return 0; + } + } + + memcpy(buf->buf + buf->buflen, buffer, size * nmemb); + buf->buflen += size * nmemb; + buf->buf[buf->buflen] = '\0'; + + return size * nmemb; +} + +void cleanup(check_curl_global_state global_state) { + if (global_state.status_line_initialized) { + curlhelp_free_statusline(global_state.status_line); + } + global_state.status_line_initialized = false; + + if (global_state.curl_easy_initialized) { + curl_easy_cleanup(global_state.curl); + } + global_state.curl_easy_initialized = false; + + if (global_state.curl_global_initialized) { + curl_global_cleanup(); + } + global_state.curl_global_initialized = false; + + if (global_state.body_buf_initialized) { + curlhelp_freewritebuffer(global_state.body_buf); + } + global_state.body_buf_initialized = false; + + if (global_state.header_buf_initialized) { + curlhelp_freewritebuffer(global_state.header_buf); + } + global_state.header_buf_initialized = false; + + if (global_state.put_buf_initialized) { + curlhelp_freereadbuffer(global_state.put_buf); + } + global_state.put_buf_initialized = false; + + if (global_state.header_list) { + curl_slist_free_all(global_state.header_list); + } + + if (global_state.host) { + curl_slist_free_all(global_state.host); + } +} + +int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family) { + struct addrinfo hints = { + .ai_family = addr_family, + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_CANONNAME, + }; + + struct addrinfo *result; + int errcode = getaddrinfo(host, NULL, &hints, &result); + if (errcode != 0) { + return errcode; + } + + strcpy(buf, ""); + struct addrinfo *res = result; + + size_t buflen_remaining = buflen - 1; + size_t addrstr_len; + char addrstr[100]; + void *ptr = {0}; + while (res) { + switch (res->ai_family) { + case AF_INET: + ptr = &((struct sockaddr_in *)res->ai_addr)->sin_addr; + break; + case AF_INET6: + ptr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; + break; + } + + inet_ntop(res->ai_family, ptr, addrstr, 100); + if (verbose >= 1) { + printf("* getaddrinfo IPv%d address: %s\n", res->ai_family == PF_INET6 ? 6 : 4, + addrstr); + } + + // Append all IPs to buf as a comma-separated string + addrstr_len = strlen(addrstr); + if (buflen_remaining > addrstr_len + 1) { + if (buf[0] != '\0') { + strncat(buf, ",", buflen_remaining); + buflen_remaining -= 1; + } + strncat(buf, addrstr, buflen_remaining); + buflen_remaining -= addrstr_len; + } + + res = res->ai_next; + } + + freeaddrinfo(result); + + return 0; +} + +/* Checks if the server 'reply' is one of the expected 'statuscodes' */ +bool expected_statuscode(const char *reply, const char *statuscodes) { + char *expected; + + if ((expected = strdup(statuscodes)) == NULL) { + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); + } + + bool result = false; + for (char *code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) { + if (strstr(reply, code) != NULL) { + result = true; + break; + } + } + + free(expected); + return result; +} + +/* returns a string "HTTP/1.x" or "HTTP/2" */ +char *string_statuscode(int major, int minor) { + static char buf[10]; + + switch (major) { + case 1: + snprintf(buf, sizeof(buf), "HTTP/%d.%d", major, minor); + break; + case 2: + case 3: + snprintf(buf, sizeof(buf), "HTTP/%d", major); + break; + default: + /* assuming here HTTP/N with N>=4 */ + snprintf(buf, sizeof(buf), "HTTP/%d", major); + break; + } + + return buf; +} + +char *perfd_time(double elapsed_time, thresholds *thlds, long socket_timeout) { + return fperfdata("time", elapsed_time, "s", (thlds->warning != NULL), + thlds->warning ? thlds->warning->end : 0, (thlds->critical != NULL), + thlds->critical ? thlds->critical->end : 0, true, 0, true, socket_timeout); +} + +char *perfd_time_connect(double elapsed_time_connect, long socket_timeout) { + return fperfdata("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, + socket_timeout); +} + +char *perfd_time_ssl(double elapsed_time_ssl, long socket_timeout) { + return fperfdata("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, + socket_timeout); +} + +char *perfd_time_headers(double elapsed_time_headers, long socket_timeout) { + return fperfdata("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, + socket_timeout); +} + +char *perfd_time_firstbyte(double elapsed_time_firstbyte, long socket_timeout) { + return fperfdata("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, + true, socket_timeout); +} + +char *perfd_time_transfer(double elapsed_time_transfer, long socket_timeout) { + return fperfdata("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, + true, socket_timeout); +} + +char *perfd_size(size_t page_len, int min_page_len) { + return perfdata("size", page_len, "B", (min_page_len > 0), min_page_len, (min_page_len > 0), 0, + true, 0, false, 0); +} + +/* check whether a file exists */ +void test_file(char *path) { + if (access(path, R_OK) == 0) { + return; + } + usage2(_("file does not exist or is not readable"), path); +} diff --git a/plugins/check_curl.d/check_curl_helpers.h b/plugins/check_curl.d/check_curl_helpers.h new file mode 100644 index 00000000..0f43ab90 --- /dev/null +++ b/plugins/check_curl.d/check_curl_helpers.h @@ -0,0 +1,125 @@ +#include "./config.h" +#include +#include "../picohttpparser/picohttpparser.h" +// #include "curl/easy.h" + +/* for buffers for header and body */ +typedef struct { + size_t buflen; + size_t bufsize; + char *buf; +} curlhelp_write_curlbuf; + +/* for buffering the data sent in PUT */ +typedef struct { + size_t buflen; + off_t pos; + char *buf; +} curlhelp_read_curlbuf; + +/* for parsing the HTTP status line */ +typedef struct { + int http_major; /* major version of the protocol, always 1 (HTTP/0.9 + * never reached the big internet most likely) */ + int http_minor; /* minor version of the protocol, usually 0 or 1 */ + int http_code; /* HTTP return code as in RFC 2145 */ + int http_subcode; /* Microsoft IIS extension, HTTP subcodes, see + * http://support.microsoft.com/kb/318380/en-us */ + const char *msg; /* the human readable message */ + char *first_line; /* a copy of the first line */ +} curlhelp_statusline; + +typedef struct { + bool curl_global_initialized; + bool curl_easy_initialized; + + bool body_buf_initialized; + curlhelp_write_curlbuf *body_buf; + + bool header_buf_initialized; + curlhelp_write_curlbuf *header_buf; + + bool status_line_initialized; + curlhelp_statusline *status_line; + + bool put_buf_initialized; + curlhelp_read_curlbuf *put_buf; + + CURL *curl; + + struct curl_slist *header_list; + struct curl_slist *host; +} check_curl_global_state; + +/* to know the underlying SSL library used by libcurl */ +typedef enum curlhelp_ssl_library { + CURLHELP_SSL_LIBRARY_UNKNOWN, + CURLHELP_SSL_LIBRARY_OPENSSL, + CURLHELP_SSL_LIBRARY_LIBRESSL, + CURLHELP_SSL_LIBRARY_GNUTLS, + CURLHELP_SSL_LIBRARY_NSS +} curlhelp_ssl_library; + +#define MAKE_LIBCURL_VERSION(major, minor, patch) ((major) * 0x10000 + (minor) * 0x100 + (patch)) + +typedef struct { + int errorcode; + check_curl_global_state curl_state; + check_curl_working_state working_state; +} check_curl_configure_curl_wrapper; + +check_curl_configure_curl_wrapper check_curl_configure_curl(check_curl_static_curl_config config, + check_curl_working_state working_state, + bool check_cert, + bool on_redirect_dependent, + int follow_method, int max_depth); + +void handle_curl_option_return_code(CURLcode res, const char *option); + +int curlhelp_initwritebuffer(curlhelp_write_curlbuf **buf); +size_t curlhelp_buffer_write_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, + void * /*stream*/); +void curlhelp_freewritebuffer(curlhelp_write_curlbuf * /*buf*/); + +int curlhelp_initreadbuffer(curlhelp_read_curlbuf **buf, const char * /*data*/, size_t /*datalen*/); +size_t curlhelp_buffer_read_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, + void * /*stream*/); +void curlhelp_freereadbuffer(curlhelp_read_curlbuf * /*buf*/); + +curlhelp_ssl_library curlhelp_get_ssl_library(void); +const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library /*ssl_library*/); + +typedef union { + struct curl_slist *to_info; + struct curl_certinfo *to_certinfo; +} cert_ptr_union; +int net_noopenssl_check_certificate(cert_ptr_union *, int, int); + +int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * /*status_line*/); +void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/); + +char *get_header_value(const struct phr_header *headers, size_t nof_headers, const char *header); +mp_state_enum check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, + const char msg[static DEFAULT_BUFFER_SIZE], int /*maximum_age*/); +size_t get_content_length(const curlhelp_write_curlbuf *header_buf, + const curlhelp_write_curlbuf *body_buf); +int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family); +CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm); + +#define INET_ADDR_MAX_SIZE INET6_ADDRSTRLEN +const char *strrstr2(const char *haystack, const char *needle); + +void cleanup(check_curl_global_state global_state); + +bool expected_statuscode(const char *reply, const char *statuscodes); +char *string_statuscode(int major, int minor); + +char *perfd_time(double elapsed_time, thresholds * /*thlds*/, long /*socket_timeout*/); +char *perfd_time_connect(double elapsed_time_connect, long /*socket_timeout*/); +char *perfd_time_ssl(double elapsed_time_ssl, long /*socket_timeout*/); +char *perfd_time_firstbyte(double elapsed_time_firstbyte, long /*socket_timeout*/); +char *perfd_time_headers(double elapsed_time_headers, long /*socket_timeout*/); +char *perfd_time_transfer(double elapsed_time_transfer, long /*socket_timeout*/); +char *perfd_size(size_t page_len, int /*min_page_len*/); + +void test_file(char *path); diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index be25d1bb..a4b1eecf 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -49,20 +49,7 @@ typedef struct { bool no_body; } check_curl_working_state; -check_curl_working_state check_curl_working_state_init() { - check_curl_working_state result = { - .server_address = NULL, - .server_url = DEFAULT_SERVER_URL, - .host_name = NULL, - .http_method = NULL, - .http_post_data = NULL, - .virtualPort = 0, - .serverPort = HTTP_PORT, - .use_ssl = false, - .no_body = false, - }; - return result; -} +check_curl_working_state check_curl_working_state_init(); typedef struct { bool automatic_decompression; @@ -123,63 +110,4 @@ typedef struct { bool display_html; } check_curl_config; -check_curl_config check_curl_config_init() { - check_curl_config tmp = { - .initial_config = check_curl_working_state_init(), - - .curl_config = - { - .automatic_decompression = false, - .socket_timeout = DEFAULT_SOCKET_TIMEOUT, - .haproxy_protocol = false, - .sin_family = AF_UNSPEC, - .curl_http_version = CURL_HTTP_VERSION_NONE, - .http_opt_headers = NULL, - .http_opt_headers_count = 0, - .ssl_version = CURL_SSLVERSION_DEFAULT, - .client_cert = NULL, - .client_privkey = NULL, - .ca_cert = NULL, - .verify_peer_and_host = false, - .user_agent = {'\0'}, - .proxy_auth = "", - .user_auth = "", - .http_content_type = NULL, - .cookie_jar_file = NULL, - }, - .max_depth = DEFAULT_MAX_REDIRS, - .followmethod = FOLLOW_HTTP_CURL, - .followsticky = STICKY_NONE, - - .maximum_age = -1, - .regexp = {}, - .compiled_regex = {}, - .state_regex = STATE_CRITICAL, - .invert_regex = false, - .check_cert = false, - .continue_after_check_cert = false, - .days_till_exp_warn = 0, - .days_till_exp_crit = 0, - .thlds = NULL, - .min_page_len = 0, - .max_page_len = 0, - .server_expect = - { - .string = HTTP_EXPECT, - .is_present = false, - }, - .string_expect = "", - .header_expect = "", - .on_redirect_result_state = STATE_OK, - .on_redirect_dependent = true, - - .show_extended_perfdata = false, - .show_body = false, - .display_html = false, - }; - - snprintf(tmp.curl_config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", - "check_curl", NP_VERSION, VERSION, curl_version()); - - return tmp; -} +check_curl_config check_curl_config_init(); -- cgit v1.2.3-74-g34f1 From ad4faf1a9a414c657a94cafb54e5b3f1a5f5aaa2 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 12 Sep 2025 01:35:45 +0200 Subject: Fix struct access --- plugins/check_curl.c | 2 +- plugins/check_curl.d/check_curl_helpers.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index b747ec99..b0dde1eb 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -1275,7 +1275,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 66, 0) result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_3; #else - result.config.curl_http_version = CURL_HTTP_VERSION_NONE; + result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_NONE; #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 66, 0) */ } else { fprintf(stderr, "unknown http-version parameter: %s\n", optarg); diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c index 42d63bc4..29aae524 100644 --- a/plugins/check_curl.d/check_curl_helpers.c +++ b/plugins/check_curl.d/check_curl_helpers.c @@ -516,7 +516,7 @@ check_curl_configure_curl(const check_curl_static_curl_config config, result.curl_state.put_buf_initialized = true; handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_READDATA, - (void *)&result.curl_state.put_buf), + (void *)result.curl_state.put_buf), "CURLOPT_READDATA"); handle_curl_option_return_code( curl_easy_setopt(result.curl_state.curl, CURLOPT_INFILESIZE, -- cgit v1.2.3-74-g34f1 From 44b2a25a6b9fb0791ec7150100a4d51e5f129611 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 12 Sep 2025 16:37:24 +0200 Subject: check_curl: implement new output mechanism --- plugins/check_curl.c | 562 +++++++++++++++--------------- plugins/check_curl.d/check_curl_helpers.c | 251 +++++++------ plugins/check_curl.d/check_curl_helpers.h | 21 +- plugins/check_curl.d/config.h | 7 +- 4 files changed, 443 insertions(+), 398 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index b0dde1eb..722666dd 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -42,7 +42,10 @@ const char *email = "devel@monitoring-plugins.org"; #include "thresholds.h" #include #include +#include "output.h" +#include "perfdata.h" +#include #include "common.h" #include "utils.h" #include "./check_curl.d/check_curl_helpers.h" @@ -78,7 +81,6 @@ enum { // Globals int verbose = 0; -extern char msg[DEFAULT_BUFFER_SIZE]; extern char errbuf[MAX_INPUT_BUFFER]; extern bool is_openssl_callback; extern bool add_sslctx_verify_fun; @@ -93,8 +95,8 @@ typedef struct { } check_curl_config_wrapper; static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); -static mp_state_enum check_http(check_curl_config /*config*/, check_curl_working_state workingState, - int redir_depth); +static mp_subcheck check_http(check_curl_config /*config*/, check_curl_working_state workingState, + int redir_depth); typedef struct { int redir_depth; @@ -149,7 +151,12 @@ int main(int argc, char **argv) { check_curl_working_state working_state = config.initial_config; - exit((int)check_http(config, working_state, 0)); + mp_check overall = mp_check_init(); + mp_subcheck sc_test = check_http(config, working_state, 0); + + mp_add_subcheck_to_check(&overall, sc_test); + + mp_exit(overall); } #ifdef HAVE_SSL @@ -200,8 +207,8 @@ CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) { # endif /* USE_OPENSSL */ #endif /* HAVE_SSL */ -mp_state_enum check_http(const check_curl_config config, check_curl_working_state workingState, - int redir_depth) { +mp_subcheck check_http(const check_curl_config config, check_curl_working_state workingState, + int redir_depth) { // ======================= // Initialisation for curl @@ -213,6 +220,13 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat check_curl_global_state curl_state = conf_curl_struct.curl_state; workingState = conf_curl_struct.working_state; + mp_subcheck sc_result = mp_subcheck_init(); + + char *url = fmt_url(workingState); + xasprintf(&sc_result.output, "Testing %s", url); + // TODO add some output here URL or something + free(url); + // ============== // do the request // ============== @@ -222,172 +236,149 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat printf("**** REQUEST CONTENT ****\n%s\n", workingState.http_post_data); } + mp_subcheck sc_curl = mp_subcheck_init(); + /* Curl errors, result in critical Nagios state */ if (res != CURLE_OK) { - snprintf(msg, DEFAULT_BUFFER_SIZE, - _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"), - workingState.serverPort, res, errbuf[0] ? errbuf : curl_easy_strerror(res)); - die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); + xasprintf(&sc_curl.output, + _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"), + workingState.serverPort, res, errbuf[0] ? errbuf : curl_easy_strerror(res)); + sc_curl = mp_set_subcheck_state(sc_curl, STATE_CRITICAL); + mp_add_subcheck_to_subcheck(&sc_result, sc_curl); + return sc_result; } + xasprintf(&sc_curl.output, "cURL performed query"); + sc_curl = mp_set_subcheck_state(sc_curl, STATE_OK); + mp_add_subcheck_to_subcheck(&sc_result, sc_curl); + // ========== // Evaluation // ========== - mp_state_enum result_ssl = STATE_OK; -/* certificate checks */ #ifdef LIBCURL_FEATURE_SSL - if (workingState.use_ssl) { - if (config.check_cert) { - if (is_openssl_callback) { -# ifdef USE_OPENSSL - /* check certificate with OpenSSL functions, curl has been built against OpenSSL - * and we actually have OpenSSL in the monitoring tools - */ - result_ssl = np_net_ssl_check_certificate(cert, config.days_till_exp_warn, - config.days_till_exp_crit); - if (!config.continue_after_check_cert) { - return result_ssl; - } -# else /* USE_OPENSSL */ - die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL " - "callback used and not linked against OpenSSL\n"); -# endif /* USE_OPENSSL */ - } else { - struct curl_slist *slist; + if (workingState.use_ssl && config.check_cert) { + mp_subcheck sc_certificate = check_curl_certificate_checks( + curl_state.curl, cert, config.days_till_exp_warn, config.days_till_exp_crit); - cert_ptr_union cert_ptr = {0}; - cert_ptr.to_info = NULL; - res = curl_easy_getinfo(curl_state.curl, CURLINFO_CERTINFO, &cert_ptr.to_info); - if (!res && cert_ptr.to_info) { -# ifdef USE_OPENSSL - /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert - * parsing We only check the first certificate and assume it's the one of - * the server - */ - char *raw_cert = NULL; - bool got_first_cert = false; - for (int i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) { - if (got_first_cert) { - break; - } - - for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; - slist = slist->next) { - if (verbose >= 2) { - printf("%d ** %s\n", i, slist->data); - } - if (strncmp(slist->data, "Cert:", 5) == 0) { - raw_cert = &slist->data[5]; - got_first_cert = true; - break; - } - } - } - - if (!raw_cert) { - snprintf(msg, DEFAULT_BUFFER_SIZE, - _("Cannot retrieve certificates from CERTINFO information - " - "certificate data was empty")); - die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); - } - BIO *cert_BIO = BIO_new(BIO_s_mem()); - BIO_write(cert_BIO, raw_cert, (int)strlen(raw_cert)); - cert = PEM_read_bio_X509(cert_BIO, NULL, NULL, NULL); - if (!cert) { - snprintf( - msg, DEFAULT_BUFFER_SIZE, - _("Cannot read certificate from CERTINFO information - BIO error")); - die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); - } - BIO_free(cert_BIO); - result_ssl = np_net_ssl_check_certificate(cert, config.days_till_exp_warn, - config.days_till_exp_crit); - if (!config.continue_after_check_cert) { - return result_ssl; - } -# else /* USE_OPENSSL */ - /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our - * disposal, so we use the libcurl CURLINFO data - */ - result_ssl = net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn, - days_till_exp_crit); - if (!continue_after_check_cert) { - return result_ssl; - } -# endif /* USE_OPENSSL */ - } else { - snprintf(msg, DEFAULT_BUFFER_SIZE, - _("Cannot retrieve certificates - cURL returned %d - %s"), res, - curl_easy_strerror(res)); - die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); - } - } + mp_add_subcheck_to_subcheck(&sc_result, sc_certificate); + if (!config.continue_after_check_cert) { + // TODO finish here then } } -#endif /* LIBCURL_FEATURE_SSL */ +#endif /* we got the data and we executed the request in a given time, so we can append * performance data to the answer always */ + + // total time the query took + mp_perfdata pd_total_time = perfdata_init(); double total_time; handle_curl_option_return_code( curl_easy_getinfo(curl_state.curl, CURLINFO_TOTAL_TIME, &total_time), "CURLINFO_TOTAL_TIME"); - size_t page_len = get_content_length(curl_state.header_buf, curl_state.body_buf); - char perfstring[DEFAULT_BUFFER_SIZE]; + mp_perfdata_value pd_val_total_time = mp_create_pd_value(total_time); + pd_total_time.value = pd_val_total_time; + pd_total_time = mp_pd_set_thresholds(pd_total_time, config.thlds); + pd_total_time.label = "time"; + pd_total_time.uom = "s"; + + mp_subcheck sc_total_time = mp_subcheck_init(); + sc_total_time = mp_set_subcheck_state(sc_total_time, mp_get_pd_status(pd_total_time)); + xasprintf(&sc_total_time.output, "Total connection time: %fs", total_time); + mp_add_perfdata_to_subcheck(&sc_total_time, pd_total_time); + + mp_add_subcheck_to_subcheck(&sc_result, sc_total_time); + if (config.show_extended_perfdata) { + // overall connection time + mp_perfdata pd_time_connect = perfdata_init(); double time_connect; handle_curl_option_return_code( curl_easy_getinfo(curl_state.curl, CURLINFO_CONNECT_TIME, &time_connect), "CURLINFO_CONNECT_TIME"); + mp_perfdata_value pd_val_time_connect = mp_create_pd_value(time_connect); + pd_time_connect.value = pd_val_time_connect; + pd_time_connect.label = "time"; + pd_time_connect.uom = "s"; + pd_time_connect = mp_set_pd_max_value( + pd_time_connect, mp_create_pd_value(config.curl_config.socket_timeout)); + + pd_time_connect = mp_pd_set_thresholds(pd_time_connect, config.thlds); + mp_add_perfdata_to_subcheck(&sc_result, pd_time_connect); + + // application connection time, used to compute other timings double time_appconnect; handle_curl_option_return_code( curl_easy_getinfo(curl_state.curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), "CURLINFO_APPCONNECT_TIME"); - double time_headers; - handle_curl_option_return_code( - curl_easy_getinfo(curl_state.curl, CURLINFO_PRETRANSFER_TIME, &time_headers), - "CURLINFO_PRETRANSFER_TIME"); + if (workingState.use_ssl) { + mp_perfdata pd_time_tls = perfdata_init(); + { + mp_perfdata_value pd_val_time_tls = + mp_create_pd_value(time_appconnect - time_connect); + + pd_time_tls.value = pd_val_time_tls; + } + pd_time_tls.label = "time_tls"; + pd_time_tls.uom = "s"; + mp_add_perfdata_to_subcheck(&sc_result, pd_time_tls); + } + + mp_perfdata pd_time_headers = perfdata_init(); + { + double time_headers; + handle_curl_option_return_code( + curl_easy_getinfo(curl_state.curl, CURLINFO_PRETRANSFER_TIME, &time_headers), + "CURLINFO_PRETRANSFER_TIME"); + + mp_perfdata_value pd_val_time_headers = + mp_create_pd_value(time_headers - time_appconnect); + + pd_time_headers.value = pd_val_time_headers; + } + pd_time_headers.label = "time_headers"; + pd_time_headers.uom = "s"; + mp_add_perfdata_to_subcheck(&sc_result, pd_time_headers); + mp_perfdata pd_time_firstbyte = perfdata_init(); double time_firstbyte; handle_curl_option_return_code( curl_easy_getinfo(curl_state.curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), "CURLINFO_STARTTRANSFER_TIME"); - snprintf( - perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s", - perfd_time(total_time, config.thlds, config.curl_config.socket_timeout), - perfd_size(page_len, config.min_page_len), - perfd_time_connect(time_connect, config.curl_config.socket_timeout), - workingState.use_ssl - ? perfd_time_ssl(time_appconnect - time_connect, config.curl_config.socket_timeout) - : "", - perfd_time_headers(time_headers - time_appconnect, config.curl_config.socket_timeout), - perfd_time_firstbyte(time_firstbyte - time_headers, config.curl_config.socket_timeout), - perfd_time_transfer(total_time - time_firstbyte, config.curl_config.socket_timeout)); - } else { - snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s", - perfd_time(total_time, config.thlds, config.curl_config.socket_timeout), - perfd_size(page_len, config.min_page_len)); + mp_perfdata_value pd_val_time_firstbyte = mp_create_pd_value(time_firstbyte); + pd_time_firstbyte.value = pd_val_time_firstbyte; + pd_time_firstbyte.label = "time_firstbyte"; + pd_time_firstbyte.uom = "s"; + mp_add_perfdata_to_subcheck(&sc_result, pd_time_firstbyte); + + mp_perfdata pd_time_transfer = perfdata_init(); + pd_time_transfer.value = mp_create_pd_value(total_time - time_firstbyte); + pd_time_transfer.label = "time_transfer"; + pd_time_transfer.uom = "s"; + mp_add_perfdata_to_subcheck(&sc_result, pd_time_transfer); } /* return a CRITICAL status if we couldn't read any data */ if (strlen(curl_state.header_buf->buf) == 0 && strlen(curl_state.body_buf->buf) == 0) { - die(STATE_CRITICAL, _("HTTP CRITICAL - No header received from host\n")); + sc_result = mp_set_subcheck_state(sc_result, STATE_CRITICAL); + xasprintf(&sc_result.output, "No header received from host"); + return sc_result; } /* get status line of answer, check sanity of HTTP code */ if (curlhelp_parse_statusline(curl_state.header_buf->buf, curl_state.status_line) < 0) { - snprintf(msg, DEFAULT_BUFFER_SIZE, - "Unparsable status line in %.3g seconds response time|%s\n", total_time, - perfstring); + sc_result = mp_set_subcheck_state(sc_result, STATE_CRITICAL); /* we cannot know the major/minor version here for sure as we cannot parse the first * line */ - die(STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x unknown - %s", msg); + xasprintf(&sc_result.output, "HTTP/x.x unknown - Unparsable status line"); + return sc_result; } + curl_state.status_line_initialized = true; /* get result code from cURL */ @@ -406,43 +397,71 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } /* make sure the status line matches the response we are looking for */ + mp_subcheck sc_expect = mp_subcheck_init(); + sc_expect = mp_set_subcheck_default_state(sc_expect, STATE_OK); if (!expected_statuscode(curl_state.status_line->first_line, config.server_expect.string)) { if (workingState.serverPort == HTTP_PORT) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), - curl_state.status_line->first_line); + xasprintf(&sc_expect.output, _("Invalid HTTP response received from host: %s\n"), + curl_state.status_line->first_line); } else { - snprintf(msg, DEFAULT_BUFFER_SIZE, - _("Invalid HTTP response received from host on port %d: %s\n"), - workingState.serverPort, curl_state.status_line->first_line); + xasprintf(&sc_expect.output, + _("Invalid HTTP response received from host on port %d: %s\n"), + workingState.serverPort, curl_state.status_line->first_line); } - die(STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg, config.show_body ? "\n" : "", - config.show_body ? curl_state.body_buf->buf : ""); + sc_expect = mp_set_subcheck_default_state(sc_expect, STATE_CRITICAL); + } else { + xasprintf(&sc_expect.output, _("Status line output matched \"%s\""), + config.server_expect.string); } + mp_add_subcheck_to_subcheck(&sc_result, sc_expect); - mp_state_enum result = STATE_OK; - if (config.server_expect.is_present) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), - config.server_expect.string); - if (verbose) { - printf("%s\n", msg); - } - result = STATE_OK; - } else { + if (!config.server_expect.is_present) { /* illegal return codes result in a critical state */ + mp_subcheck sc_return_code = mp_subcheck_init(); + sc_return_code = mp_set_subcheck_default_state(sc_return_code, STATE_OK); + xasprintf(&sc_return_code.output, "HTTP return code: %d", + curl_state.status_line->http_code); + if (httpReturnCode >= 600 || httpReturnCode < 100) { - die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), - curl_state.status_line->http_code, curl_state.status_line->msg); - /* server errors result in a critical state */ - } else if (httpReturnCode >= 500) { - result = STATE_CRITICAL; + sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_CRITICAL); + xasprintf(&sc_return_code.output, _("Invalid Status (%d, %.40s)"), + curl_state.status_line->http_code, curl_state.status_line->msg); + mp_add_subcheck_to_subcheck(&sc_result, sc_return_code); + return sc_result; + } + + // server errors result in a critical state + if (httpReturnCode >= 500) { + sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_CRITICAL); /* client errors result in a warning state */ } else if (httpReturnCode >= 400) { - result = STATE_WARNING; + sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_WARNING); /* check redirected page if specified */ } else if (httpReturnCode >= 300) { if (config.on_redirect_dependent) { if (config.followmethod == FOLLOW_LIBCURL) { httpReturnCode = curl_state.status_line->http_code; + handle_curl_option_return_code( + curl_easy_getinfo(curl_state.curl, CURLINFO_REDIRECT_COUNT, &redir_depth), + "CURLINFO_REDIRECT_COUNT"); + + if (verbose >= 2) { + printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth); + } + + mp_subcheck sc_redir_depth = mp_subcheck_init(); + if (redir_depth > config.max_depth) { + xasprintf(&sc_redir_depth.output, + "maximum redirection depth %d exceeded in libcurl", + config.max_depth); + sc_redir_depth = mp_set_subcheck_state(sc_redir_depth, STATE_CRITICAL); + mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth); + return sc_result; + } + xasprintf(&sc_redir_depth.output, "redirection depth %d (of a maximum %d)", + redir_depth, config.max_depth); + mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth); + } else { /* old check_http style redirection, if we come * back here, we are in the same status as with @@ -451,54 +470,53 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat redir_wrapper redir_result = redir(curl_state.header_buf, config, redir_depth, workingState); cleanup(curl_state); - check_http(config, redir_result.working_state, redir_result.redir_depth); + mp_subcheck sc_redir = + check_http(config, redir_result.working_state, redir_result.redir_depth); + mp_add_subcheck_to_subcheck(&sc_result, sc_redir); + + return sc_result; } } else { /* this is a specific code in the command line to * be returned when a redirection is encountered */ + sc_return_code = + mp_set_subcheck_state(sc_return_code, config.on_redirect_result_state); } - result = max_state_alt(config.on_redirect_result_state, result); - /* all other codes are considered ok */ } else { - result = STATE_OK; - } - } - - /* libcurl redirection internally, handle error states here */ - if (config.followmethod == FOLLOW_LIBCURL) { - handle_curl_option_return_code( - curl_easy_getinfo(curl_state.curl, CURLINFO_REDIRECT_COUNT, &redir_depth), - "CURLINFO_REDIRECT_COUNT"); - - if (verbose >= 2) { - printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth); + sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_OK); } - if (redir_depth > config.max_depth) { - snprintf(msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl", - config.max_depth); - die(STATE_WARNING, "HTTP WARNING - %s", msg); - } + mp_add_subcheck_to_subcheck(&sc_result, sc_return_code); } /* check status codes, set exit status accordingly */ if (curl_state.status_line->http_code != httpReturnCode) { - die(STATE_CRITICAL, _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"), - string_statuscode(curl_state.status_line->http_major, curl_state.status_line->http_minor), - curl_state.status_line->http_code, curl_state.status_line->msg, httpReturnCode); + mp_subcheck sc_http_return_code_sanity = mp_subcheck_init(); + sc_http_return_code_sanity = + mp_set_subcheck_state(sc_http_return_code_sanity, STATE_CRITICAL); + xasprintf(&sc_http_return_code_sanity.output, + _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"), + string_statuscode(curl_state.status_line->http_major, + curl_state.status_line->http_minor), + curl_state.status_line->http_code, curl_state.status_line->msg, httpReturnCode); + + mp_add_subcheck_to_subcheck(&sc_result, sc_http_return_code_sanity); + return sc_result; } if (config.maximum_age >= 0) { - result = max_state_alt( - check_document_dates(curl_state.header_buf, msg, config.maximum_age), result); + mp_subcheck sc_max_age = check_document_dates(curl_state.header_buf, config.maximum_age); + mp_add_subcheck_to_subcheck(&sc_result, sc_max_age); } /* Page and Header content checks go here */ - if (strlen(config.header_expect)) { - if (!strstr(curl_state.header_buf->buf, config.header_expect)) { + mp_subcheck sc_header_expect = mp_subcheck_init(); + sc_header_expect = mp_set_subcheck_default_state(sc_header_expect, STATE_OK); + xasprintf(&sc_header_expect.output, "Expect %s in header", config.header_expect); + if (!strstr(curl_state.header_buf->buf, config.header_expect)) { char output_header_search[30] = ""; strncpy(&output_header_search[0], config.header_expect, sizeof(output_header_search)); @@ -506,22 +524,23 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat bcopy("...", &output_header_search[sizeof(output_header_search) - 4], 4); } - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sheader '%s' not found on '%s://%s:%d%s', "), - msg, output_header_search, workingState.use_ssl ? "https" : "http", - workingState.host_name ? workingState.host_name : workingState.server_address, - workingState.serverPort, workingState.server_url); - - strcpy(msg, tmp); + xasprintf(&sc_header_expect.output, _("header '%s' not found on '%s://%s:%d%s', "), + output_header_search, workingState.use_ssl ? "https" : "http", + workingState.host_name ? workingState.host_name : workingState.server_address, + workingState.serverPort, workingState.server_url); - result = STATE_CRITICAL; + sc_header_expect = mp_set_subcheck_state(sc_header_expect, STATE_CRITICAL); } + + mp_add_subcheck_to_subcheck(&sc_result, sc_header_expect); } if (strlen(config.string_expect)) { - if (!strstr(curl_state.body_buf->buf, config.string_expect)) { + mp_subcheck sc_string_expect = mp_subcheck_init(); + sc_string_expect = mp_set_subcheck_default_state(sc_string_expect, STATE_OK); + xasprintf(&sc_string_expect.output, "Expect string \"%s\" in body", config.string_expect); + if (!strstr(curl_state.body_buf->buf, config.string_expect)) { char output_string_search[30] = ""; strncpy(&output_string_search[0], config.string_expect, sizeof(output_string_search)); @@ -529,93 +548,86 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat bcopy("...", &output_string_search[sizeof(output_string_search) - 4], 4); } - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sstring '%s' not found on '%s://%s:%d%s', "), - msg, output_string_search, workingState.use_ssl ? "https" : "http", - workingState.host_name ? workingState.host_name : workingState.server_address, - workingState.serverPort, workingState.server_url); + xasprintf(&sc_string_expect.output, _("string '%s' not found on '%s://%s:%d%s', "), + output_string_search, workingState.use_ssl ? "https" : "http", + workingState.host_name ? workingState.host_name : workingState.server_address, + workingState.serverPort, workingState.server_url); - strcpy(msg, tmp); - - result = STATE_CRITICAL; + sc_string_expect = mp_set_subcheck_state(sc_string_expect, STATE_CRITICAL); } + + mp_add_subcheck_to_subcheck(&sc_result, sc_string_expect); } if (strlen(config.regexp)) { + mp_subcheck sc_body_regex = mp_subcheck_init(); + xasprintf(&sc_body_regex.output, "Regex \"%s\" in body matched", config.regexp); regmatch_t pmatch[REGS]; + int errcode = regexec(&config.compiled_regex, curl_state.body_buf->buf, REGS, pmatch, 0); - if ((errcode == 0 && !config.invert_regex) || - (errcode == REG_NOMATCH && config.invert_regex)) { - /* OK - No-op to avoid changing the logic around it */ - result = max_state_alt(STATE_OK, result); - } else if ((errcode == REG_NOMATCH && !config.invert_regex) || - (errcode == 0 && config.invert_regex)) { - if (!config.invert_regex) { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spattern not found, "), msg); - strcpy(msg, tmp); + if (errcode == 0) { + // got a match + if (config.invert_regex) { + sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK); } else { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spattern found, "), msg); - strcpy(msg, tmp); + sc_body_regex = mp_set_subcheck_state(sc_body_regex, config.state_regex); } - result = config.state_regex; } else { - regerror(errcode, &config.compiled_regex, errbuf, MAX_INPUT_BUFFER); - - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sExecute Error: %s, "), msg, errbuf); - strcpy(msg, tmp); - result = STATE_UNKNOWN; + xasprintf(&sc_body_regex.output, "%s not", sc_body_regex.output); + // got no match + if (config.invert_regex) { + sc_body_regex = mp_set_subcheck_state(sc_body_regex, config.state_regex); + } else { + sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK); + } } - } - /* make sure the page is of an appropriate size */ - if ((config.max_page_len > 0) && (page_len > config.max_page_len)) { - char tmp[DEFAULT_BUFFER_SIZE]; + mp_add_subcheck_to_subcheck(&sc_result, sc_body_regex); + } - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %zu too large, "), msg, page_len); + // size a.k.a. page length + mp_perfdata pd_page_length = perfdata_init(); + size_t page_len = get_content_length(curl_state.header_buf, curl_state.body_buf); + mp_perfdata_value pd_val_page_length = mp_create_pd_value(page_len); + pd_page_length.value = pd_val_page_length; + pd_page_length.label = "size"; + pd_page_length.uom = "B"; + pd_page_length.min = mp_create_pd_value(0); + pd_page_length.warn = config.page_length_limits; + pd_page_length.warn_present = true; - strcpy(msg, tmp); + /* make sure the page is of an appropriate size */ + if (config.page_length_limits_is_set) { + mp_thresholds page_length_threshold = mp_thresholds_init(); + page_length_threshold.warning = config.page_length_limits; + page_length_threshold.warning_is_set = true; - result = max_state_alt(STATE_WARNING, result); + pd_page_length = mp_pd_set_thresholds(pd_page_length, page_length_threshold); - } else if ((config.min_page_len > 0) && (page_len < config.min_page_len)) { - char tmp[DEFAULT_BUFFER_SIZE]; + mp_subcheck sc_page_length = mp_subcheck_init(); - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %zu too small, "), msg, page_len); - strcpy(msg, tmp); - result = max_state_alt(STATE_WARNING, result); - } + mp_add_perfdata_to_subcheck(&sc_page_length, pd_page_length); - /* -w, -c: check warning and critical level */ - result = max_state_alt(get_status(total_time, config.thlds), result); + mp_state_enum tmp_state = mp_get_pd_status(pd_page_length); + sc_page_length = mp_set_subcheck_state(sc_page_length, tmp_state); - /* Cut-off trailing characters */ - if (strlen(msg) >= 2) { - if (msg[strlen(msg) - 2] == ',') { - msg[strlen(msg) - 2] = '\0'; - } else { - msg[strlen(msg) - 3] = '\0'; + switch (tmp_state) { + case STATE_CRITICAL: + case STATE_WARNING: + xasprintf(&sc_page_length.output, _("page size %zu violates threshold"), page_len); + break; + case STATE_OK: + xasprintf(&sc_page_length.output, _("page size %zu is OK"), page_len); + break; + default: + assert(false); } + + mp_add_subcheck_to_subcheck(&sc_result, sc_page_length); } - /* TODO: separate _() msg and status code: die (result, "HTTP %s: %s\n", state_text(result), - * msg); */ - die((int)max_state_alt(result, result_ssl), - "HTTP %s: %s %d %s%s%s - %zu bytes in %.3f second response time %s|%s\n%s%s", - state_text(result), - string_statuscode(curl_state.status_line->http_major, curl_state.status_line->http_minor), - curl_state.status_line->http_code, curl_state.status_line->msg, strlen(msg) > 0 ? " - " : "", - msg, page_len, total_time, (config.display_html ? "" : ""), perfstring, - (config.show_body ? curl_state.body_buf->buf : ""), (config.show_body ? "\n" : "")); - - return max_state_alt(result, result_ssl); + return sc_result; } int uri_strcmp(const UriTextRangeA range, const char *stringToCompare) { @@ -895,8 +907,6 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } int option = 0; - char *warning_thresholds = NULL; - char *critical_thresholds = NULL; int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE; bool specify_port = false; bool enable_tls = false; @@ -931,11 +941,22 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } break; case 'c': /* critical time threshold */ - critical_thresholds = optarg; - break; + { + mp_range_parsed critical_range = mp_parse_range_string(optarg); + if (critical_range.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse critical threshold: %s", optarg); + } + result.config.thlds = mp_thresholds_set_crit(result.config.thlds, critical_range.range); + } break; case 'w': /* warning time threshold */ - warning_thresholds = optarg; - break; + { + mp_range_parsed warning_range = mp_parse_range_string(optarg); + + if (warning_range.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse warning threshold: %s", optarg); + } + result.config.thlds = mp_thresholds_set_warn(result.config.thlds, warning_range.range); + } break; case 'H': /* virtual host */ result.config.initial_config.host_name = strdup(optarg); char *tmp_string; @@ -1207,27 +1228,14 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { break; case 'm': /* min_page_length */ { - char *tmp; - if (strchr(optarg, ':') != (char *)NULL) { - /* range, so get two values, min:max */ - tmp = strtok(optarg, ":"); - if (tmp == NULL) { - printf("Bad format: try \"-m min:max\"\n"); - exit(STATE_WARNING); - } else { - result.config.min_page_len = atol(tmp); - } + mp_range_parsed foo = mp_parse_range_string(optarg); - tmp = strtok(NULL, ":"); - if (tmp == NULL) { - printf("Bad format: try \"-m min:max\"\n"); - exit(STATE_WARNING); - } else { - result.config.max_page_len = atol(tmp); - } - } else { - result.config.min_page_len = atol(optarg); + if (foo.error != MP_PARSING_SUCCES) { + die(STATE_CRITICAL, "failed to parse page size limits: %s", optarg); } + + result.config.page_length_limits = foo.range; + result.config.page_length_limits_is_set = true; break; } case 'N': /* no-body */ @@ -1401,16 +1409,6 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } } - set_thresholds(&result.config.thlds, warning_thresholds, critical_thresholds); - - if (critical_thresholds && - result.config.thlds->critical->end > (double)result.config.curl_config.socket_timeout) { - result.config.curl_config.socket_timeout = (int)result.config.thlds->critical->end + 1; - } - if (verbose >= 2) { - printf("* Socket timeout set to %ld seconds\n", result.config.curl_config.socket_timeout); - } - if (result.config.initial_config.http_method == NULL) { result.config.initial_config.http_method = strdup("GET"); } diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c index e33f2aa9..49949ccb 100644 --- a/plugins/check_curl.d/check_curl_helpers.c +++ b/plugins/check_curl.d/check_curl_helpers.c @@ -5,9 +5,12 @@ #include #include #include "../utils.h" +#include "check_curl.d/config.h" +#include "output.h" +#include "perfdata.h" +#include "states.h" extern int verbose; -char msg[DEFAULT_BUFFER_SIZE]; char errbuf[MAX_INPUT_BUFFER]; bool is_openssl_callback = false; bool add_sslctx_verify_fun = false; @@ -127,10 +130,9 @@ check_curl_configure_curl(const check_curl_static_curl_config config, int res; if ((res = lookup_host(working_state.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2, config.sin_family)) != 0) { - snprintf(msg, DEFAULT_BUFFER_SIZE, - _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), - working_state.server_address, res, gai_strerror(res)); - die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); + die(STATE_CRITICAL, + _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), + working_state.server_address, res, gai_strerror(res)); } snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", working_state.host_name, working_state.serverPort, addrstr); @@ -154,12 +156,7 @@ check_curl_configure_curl(const check_curl_static_curl_config config, } /* compose URL: use the address we want to connect to, set Host: header later */ - char url[DEFAULT_BUFFER_SIZE]; - snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", working_state.use_ssl ? "https" : "http", - (working_state.use_ssl & (working_state.host_name != NULL)) - ? working_state.host_name - : working_state.server_address, - working_state.serverPort, working_state.server_url); + char *url = fmt_url(working_state); if (verbose >= 1) { printf("* curl CURLOPT_URL: %s\n", url); @@ -167,6 +164,8 @@ check_curl_configure_curl(const check_curl_static_curl_config config, handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, url), "CURLOPT_URL"); + free(url); + /* extract proxy information for legacy proxy https requests */ if (!strcmp(working_state.http_method, "CONNECT") || strstr(working_state.server_url, "http") == working_state.server_url) { @@ -548,10 +547,8 @@ check_curl_configure_curl(const check_curl_static_curl_config config, void handle_curl_option_return_code(CURLcode res, const char *option) { if (res != CURLE_OK) { - snprintf(msg, DEFAULT_BUFFER_SIZE, - _("Error while setting cURL option '%s': cURL returned %d - %s"), option, res, - curl_easy_strerror(res)); - die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); + die(STATE_CRITICAL, _("Error while setting cURL option '%s': cURL returned %d - %s"), + option, res, curl_easy_strerror(res)); } } @@ -618,9 +615,9 @@ check_curl_config check_curl_config_init() { .continue_after_check_cert = false, .days_till_exp_warn = 0, .days_till_exp_crit = 0, - .thlds = NULL, - .min_page_len = 0, - .max_page_len = 0, + .thlds = mp_thresholds_init(), + .page_length_limits = mp_range_init(), + .page_length_limits_is_set = false, .server_expect = { .string = HTTP_EXPECT, @@ -729,9 +726,7 @@ size_t get_content_length(const curlhelp_write_curlbuf *header_buf, return header_buf->buflen + body_buf->buflen; } -mp_state_enum check_document_dates(const curlhelp_write_curlbuf *header_buf, - const char msg[static DEFAULT_BUFFER_SIZE], - const int maximum_age) { +mp_subcheck check_document_dates(const curlhelp_write_curlbuf *header_buf, const int maximum_age) { struct phr_header headers[255]; size_t nof_headers = 255; curlhelp_statusline status_line; @@ -747,73 +742,54 @@ mp_state_enum check_document_dates(const curlhelp_write_curlbuf *header_buf, char *server_date = get_header_value(headers, nof_headers, "date"); char *document_date = get_header_value(headers, nof_headers, "last-modified"); - mp_state_enum date_result = STATE_OK; + mp_subcheck sc_document_dates = mp_subcheck_init(); if (!server_date || !*server_date) { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date unknown, "), msg); - strcpy(msg, tmp); - - date_result = max_state_alt(STATE_UNKNOWN, date_result); - + xasprintf(&sc_document_dates.output, _("Server date unknown")); + sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_UNKNOWN); } else if (!document_date || !*document_date) { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument modification date unknown, "), msg); - strcpy(msg, tmp); - - date_result = max_state_alt(STATE_CRITICAL, date_result); - + xasprintf(&sc_document_dates.output, _("Document modification date unknown, ")); + sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); } else { time_t srv_data = curl_getdate(server_date, NULL); time_t doc_data = curl_getdate(document_date, NULL); + if (verbose >= 2) { printf("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data, document_date, (int)doc_data); } - if (srv_data <= 0) { - char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date \"%100s\" unparsable, "), msg, - server_date); - strcpy(msg, tmp); - - date_result = max_state_alt(STATE_CRITICAL, date_result); + if (srv_data <= 0) { + xasprintf(&sc_document_dates.output, _("Server date \"%100s\" unparsable"), + server_date); + sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); } else if (doc_data <= 0) { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument date \"%100s\" unparsable, "), msg, - document_date); - strcpy(msg, tmp); - date_result = max_state_alt(STATE_CRITICAL, date_result); + xasprintf(&sc_document_dates.output, _("Document date \"%100s\" unparsable"), + document_date); + sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); } else if (doc_data > srv_data + 30) { - char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument is %d seconds in the future, "), msg, - (int)doc_data - (int)srv_data); - strcpy(msg, tmp); + xasprintf(&sc_document_dates.output, _("Document is %d seconds in the future"), + (int)doc_data - (int)srv_data); - date_result = max_state_alt(STATE_CRITICAL, date_result); + sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); } else if (doc_data < srv_data - maximum_age) { time_t last_modified = (srv_data - doc_data); - if (last_modified > (60 * 60 * 24 * 2)) { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), msg, - ((float)last_modified) / (60 * 60 * 24)); - strcpy(msg, tmp); - - date_result = max_state_alt(STATE_CRITICAL, date_result); + if (last_modified > (60 * 60 * 24 * 2)) { // two days hardcoded? + xasprintf(&sc_document_dates.output, _("Last modified %.1f days ago"), + ((float)last_modified) / (60 * 60 * 24)); + sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); } else { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %ld:%02ld:%02ld ago, "), msg, - last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60); - strcpy(msg, tmp); - - date_result = max_state_alt(STATE_CRITICAL, date_result); + xasprintf(&sc_document_dates.output, _("Last modified %ld:%02ld:%02ld ago"), + last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60); + sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); } + } else { + // TODO is this the OK case? + time_t last_modified = (srv_data - doc_data); + xasprintf(&sc_document_dates.output, _("Last modified %ld:%02ld:%02ld ago"), + last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60); + sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_OK); } } @@ -824,7 +800,7 @@ mp_state_enum check_document_dates(const curlhelp_write_curlbuf *header_buf, free(document_date); } - return date_result; + return sc_document_dates; } void curlhelp_free_statusline(curlhelp_statusline *status_line) { free(status_line->first_line); } @@ -1172,46 +1148,117 @@ char *string_statuscode(int major, int minor) { return buf; } -char *perfd_time(double elapsed_time, thresholds *thlds, long socket_timeout) { - return fperfdata("time", elapsed_time, "s", (thlds->warning != NULL), - thlds->warning ? thlds->warning->end : 0, (thlds->critical != NULL), - thlds->critical ? thlds->critical->end : 0, true, 0, true, socket_timeout); +/* check whether a file exists */ +void test_file(char *path) { + if (access(path, R_OK) == 0) { + return; + } + usage2(_("file does not exist or is not readable"), path); } -char *perfd_time_connect(double elapsed_time_connect, long socket_timeout) { - return fperfdata("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, - socket_timeout); -} +mp_subcheck np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, + int days_till_exp_crit); -char *perfd_time_ssl(double elapsed_time_ssl, long socket_timeout) { - return fperfdata("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, - socket_timeout); -} +mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_till_exp, + int crit_days_till_exp) { + mp_subcheck sc_cert_result = mp_subcheck_init(); + sc_cert_result = mp_set_subcheck_default_state(sc_cert_result, STATE_OK); -char *perfd_time_headers(double elapsed_time_headers, long socket_timeout) { - return fperfdata("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, - socket_timeout); -} +#ifdef LIBCURL_FEATURE_SSL + if (is_openssl_callback) { +# ifdef USE_OPENSSL + /* check certificate with OpenSSL functions, curl has been built against OpenSSL + * and we actually have OpenSSL in the monitoring tools + */ + return np_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp); +# else /* USE_OPENSSL */ + xasprintf(&result.output, "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL " + "callback used and not linked against OpenSSL\n"); + mp_set_subcheck_state(result, STATE_CRITICAL); +# endif /* USE_OPENSSL */ + } else { + struct curl_slist *slist; + + cert_ptr_union cert_ptr = {0}; + cert_ptr.to_info = NULL; + CURLcode res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &cert_ptr.to_info); + if (!res && cert_ptr.to_info) { +# ifdef USE_OPENSSL + /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert + * parsing We only check the first certificate and assume it's the one of + * the server + */ + char *raw_cert = NULL; + bool got_first_cert = false; + for (int i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) { + if (got_first_cert) { + break; + } + + for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) { + if (verbose >= 2) { + printf("%d ** %s\n", i, slist->data); + } + if (strncmp(slist->data, "Cert:", 5) == 0) { + raw_cert = &slist->data[5]; + got_first_cert = true; + break; + } + } + } -char *perfd_time_firstbyte(double elapsed_time_firstbyte, long socket_timeout) { - return fperfdata("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, - true, socket_timeout); -} + if (!raw_cert) { -char *perfd_time_transfer(double elapsed_time_transfer, long socket_timeout) { - return fperfdata("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, - true, socket_timeout); -} + xasprintf(&sc_cert_result.output, + _("Cannot retrieve certificates from CERTINFO information - " + "certificate data was empty")); + sc_cert_result = mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL); + return sc_cert_result; + } + + BIO *cert_BIO = BIO_new(BIO_s_mem()); + BIO_write(cert_BIO, raw_cert, (int)strlen(raw_cert)); + + cert = PEM_read_bio_X509(cert_BIO, NULL, NULL, NULL); + if (!cert) { + xasprintf(&sc_cert_result.output, + _("Cannot read certificate from CERTINFO information - BIO error")); + sc_cert_result = mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL); + return sc_cert_result; + } -char *perfd_size(size_t page_len, int min_page_len) { - return perfdata("size", page_len, "B", (min_page_len > 0), min_page_len, (min_page_len > 0), 0, - true, 0, false, 0); + BIO_free(cert_BIO); + return np_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp); +# else /* USE_OPENSSL */ + /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our + * disposal, so we use the libcurl CURLINFO data + */ + return net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn, + days_till_exp_crit); +# endif /* USE_OPENSSL */ + } else { + xasprintf(&sc_cert_result.output, + _("Cannot retrieve certificates - cURL returned %d - %s"), res, + curl_easy_strerror(res)); + mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL); + } + } +#endif /* LIBCURL_FEATURE_SSL */ + + return sc_cert_result; } -/* check whether a file exists */ -void test_file(char *path) { - if (access(path, R_OK) == 0) { - return; +char *fmt_url(check_curl_working_state workingState) { + char *url = calloc(DEFAULT_BUFFER_SIZE, sizeof(char)); + if (url == NULL) { + die(STATE_UNKNOWN, "memory allocation failed"); } - usage2(_("file does not exist or is not readable"), path); + + snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", workingState.use_ssl ? "https" : "http", + (workingState.use_ssl & (workingState.host_name != NULL)) + ? workingState.host_name + : workingState.server_address, + workingState.serverPort, workingState.server_url); + + return url; } diff --git a/plugins/check_curl.d/check_curl_helpers.h b/plugins/check_curl.d/check_curl_helpers.h index 0f43ab90..87e45a9d 100644 --- a/plugins/check_curl.d/check_curl_helpers.h +++ b/plugins/check_curl.d/check_curl_helpers.h @@ -1,7 +1,11 @@ #include "./config.h" #include #include "../picohttpparser/picohttpparser.h" -// #include "curl/easy.h" +#include "output.h" + +#if defined(HAVE_SSL) && defined(USE_OPENSSL) +# include +#endif /* for buffers for header and body */ typedef struct { @@ -99,8 +103,8 @@ int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * /*stat void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/); char *get_header_value(const struct phr_header *headers, size_t nof_headers, const char *header); -mp_state_enum check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, - const char msg[static DEFAULT_BUFFER_SIZE], int /*maximum_age*/); +mp_subcheck check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, + int /*maximum_age*/); size_t get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_write_curlbuf *body_buf); int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family); @@ -114,12 +118,7 @@ void cleanup(check_curl_global_state global_state); bool expected_statuscode(const char *reply, const char *statuscodes); char *string_statuscode(int major, int minor); -char *perfd_time(double elapsed_time, thresholds * /*thlds*/, long /*socket_timeout*/); -char *perfd_time_connect(double elapsed_time_connect, long /*socket_timeout*/); -char *perfd_time_ssl(double elapsed_time_ssl, long /*socket_timeout*/); -char *perfd_time_firstbyte(double elapsed_time_firstbyte, long /*socket_timeout*/); -char *perfd_time_headers(double elapsed_time_headers, long /*socket_timeout*/); -char *perfd_time_transfer(double elapsed_time_transfer, long /*socket_timeout*/); -char *perfd_size(size_t page_len, int /*min_page_len*/); - void test_file(char *path); +mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_till_exp, + int crit_days_till_exp); +char *fmt_url(check_curl_working_state workingState); diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index a4b1eecf..32399b8a 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -8,6 +8,7 @@ #include #include #include "curl/curl.h" +#include "perfdata.h" #include "regex.h" enum { @@ -93,9 +94,9 @@ typedef struct { bool continue_after_check_cert; int days_till_exp_warn; int days_till_exp_crit; - thresholds *thlds; - size_t min_page_len; - size_t max_page_len; + mp_thresholds thlds; + mp_range page_length_limits; + bool page_length_limits_is_set; struct { char string[MAX_INPUT_BUFFER]; bool is_present; -- cgit v1.2.3-74-g34f1 From 9afb5e2a6964aad149ca3ef0c175fcd510220505 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sat, 13 Sep 2025 12:28:49 +0200 Subject: check_curl: remove display-html option --- plugins/check_curl.c | 35 ++++++++++--------------------- plugins/check_curl.d/check_curl_helpers.c | 1 - plugins/check_curl.d/config.h | 1 - 3 files changed, 11 insertions(+), 26 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 722666dd..17fb5564 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -139,16 +139,6 @@ int main(int argc, char **argv) { const check_curl_config config = tmp_config.config; - if (config.display_html) { - printf("", - config.initial_config.use_ssl ? "https" : "http", - config.initial_config.host_name ? config.initial_config.host_name - : config.initial_config.server_address, - config.initial_config.virtualPort ? config.initial_config.virtualPort - : config.initial_config.serverPort, - config.initial_config.server_url); - } - check_curl_working_state working_state = config.initial_config; mp_check overall = mp_check_init(); @@ -672,8 +662,8 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config } if (++redir_depth > config.max_depth) { - die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s%s\n"), - config.max_depth, location, (config.display_html ? "" : "")); + die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s\n"), + config.max_depth, location); } UriParserStateA state; @@ -681,8 +671,8 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config state.uri = &uri; if (uriParseUriA(&state, location) != URI_SUCCESS) { if (state.errorCode == URI_ERROR_SYNTAX) { - die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location '%s'%s\n"), - location, (config.display_html ? "" : "")); + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location '%s'\n"), + location); } else if (state.errorCode == URI_ERROR_MALLOC) { die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); } @@ -735,8 +725,8 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config } } if (new_port > MAX_PORT) { - die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s%s\n"), MAX_PORT, - location, config.display_html ? "" : ""); + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s\n"), MAX_PORT, + location); } /* by RFC 7231 relative URLs in Location should be taken relative to @@ -772,9 +762,8 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config !strncmp(working_state.host_name, new_host, MAX_IPV4_HOSTLENGTH)) && !strcmp(working_state.server_url, new_url)) { die(STATE_CRITICAL, - _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), - working_state.use_ssl ? "https" : "http", new_host, new_port, new_url, - (config.display_html ? "" : "")); + _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s\n"), + working_state.use_ssl ? "https" : "http", new_host, new_port, new_url); } /* set new values for redirected request */ @@ -1037,10 +1026,8 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { .http_opt_headers[result.config.curl_config.http_opt_headers_count - 1] = optarg; break; case 'L': /* show html link */ - result.config.display_html = true; - break; case 'n': /* do not show html link */ - result.config.display_html = false; + // HTML link related options are deprecated break; case 'C': /* Check SSL cert validity */ #ifndef LIBCURL_FEATURE_SSL @@ -1566,8 +1553,8 @@ void print_help(void) { printf(" %s\n", _("Print additional performance data")); printf(" %s\n", "-B, --show-body"); printf(" %s\n", _("Print body content below status line")); - printf(" %s\n", "-L, --link"); - printf(" %s\n", _("Wrap output in HTML link (obsoleted by urlize)")); + // printf(" %s\n", "-L, --link"); + // printf(" %s\n", _("Wrap output in HTML link (obsoleted by urlize)")); printf(" %s\n", "-f, --onredirect="); printf(" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the")); printf(" %s\n", _("specified IP address. stickyport also ensures port stays the same.")); diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c index 49949ccb..f0a4d265 100644 --- a/plugins/check_curl.d/check_curl_helpers.c +++ b/plugins/check_curl.d/check_curl_helpers.c @@ -630,7 +630,6 @@ check_curl_config check_curl_config_init() { .show_extended_perfdata = false, .show_body = false, - .display_html = false, }; snprintf(tmp.curl_config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index 32399b8a..6100af3e 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -108,7 +108,6 @@ typedef struct { bool show_extended_perfdata; bool show_body; - bool display_html; } check_curl_config; check_curl_config check_curl_config_init(); -- cgit v1.2.3-74-g34f1 From 6ae8ba911018571afddcf51c08e3d32f5efa3b5a Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sat, 13 Sep 2025 12:49:06 +0200 Subject: check_curl: test adaption and output adaption --- plugins/check_curl.c | 39 +++++++++++--------- plugins/t/check_curl.t | 4 +-- plugins/tests/check_curl.t | 89 +++++++++++++++++++++++----------------------- 3 files changed, 69 insertions(+), 63 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 17fb5564..89173808 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -238,7 +238,28 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state return sc_result; } - xasprintf(&sc_curl.output, "cURL performed query"); + /* get status line of answer, check sanity of HTTP code */ + if (curlhelp_parse_statusline(curl_state.header_buf->buf, curl_state.status_line) < 0) { + sc_result = mp_set_subcheck_state(sc_result, STATE_CRITICAL); + /* we cannot know the major/minor version here for sure as we cannot parse the first + * line */ + xasprintf(&sc_result.output, "HTTP/x.x unknown - Unparsable status line"); + return sc_result; + } + + curl_state.status_line_initialized = true; + + size_t page_len = get_content_length(curl_state.header_buf, curl_state.body_buf); + + double total_time; + handle_curl_option_return_code( + curl_easy_getinfo(curl_state.curl, CURLINFO_TOTAL_TIME, &total_time), + "CURLINFO_TOTAL_TIME"); + + xasprintf( + &sc_curl.output, "%s %d %s - %ld bytes in %.3f second response time", + string_statuscode(curl_state.status_line->http_major, curl_state.status_line->http_minor), + curl_state.status_line->http_code, curl_state.status_line->msg, page_len, total_time); sc_curl = mp_set_subcheck_state(sc_curl, STATE_OK); mp_add_subcheck_to_subcheck(&sc_result, sc_curl); @@ -264,10 +285,6 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state // total time the query took mp_perfdata pd_total_time = perfdata_init(); - double total_time; - handle_curl_option_return_code( - curl_easy_getinfo(curl_state.curl, CURLINFO_TOTAL_TIME, &total_time), - "CURLINFO_TOTAL_TIME"); mp_perfdata_value pd_val_total_time = mp_create_pd_value(total_time); pd_total_time.value = pd_val_total_time; pd_total_time = mp_pd_set_thresholds(pd_total_time, config.thlds); @@ -360,17 +377,6 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state return sc_result; } - /* get status line of answer, check sanity of HTTP code */ - if (curlhelp_parse_statusline(curl_state.header_buf->buf, curl_state.status_line) < 0) { - sc_result = mp_set_subcheck_state(sc_result, STATE_CRITICAL); - /* we cannot know the major/minor version here for sure as we cannot parse the first - * line */ - xasprintf(&sc_result.output, "HTTP/x.x unknown - Unparsable status line"); - return sc_result; - } - - curl_state.status_line_initialized = true; - /* get result code from cURL */ long httpReturnCode; handle_curl_option_return_code( @@ -578,7 +584,6 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state // size a.k.a. page length mp_perfdata pd_page_length = perfdata_init(); - size_t page_len = get_content_length(curl_state.header_buf, curl_state.body_buf); mp_perfdata_value pd_val_page_length = mp_create_pd_value(page_len); pd_page_length.value = pd_val_page_length; pd_page_length.label = "size"; diff --git a/plugins/t/check_curl.t b/plugins/t/check_curl.t index 7a930a4e..7f1a2de0 100644 --- a/plugins/t/check_curl.t +++ b/plugins/t/check_curl.t @@ -18,7 +18,7 @@ BEGIN { } -my $successOutput = '/OK.*HTTP.*second/'; +my $successOutput = '/.*HTTP.*second/'; my $res; my $plugin = 'check_http'; @@ -63,7 +63,7 @@ $res = NPTest->testCmd( ); cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" ); # was CRITICAL only, but both check_curl and check_http print HTTP CRITICAL (puzzle?!) -like( $res->output, "/HTTP CRITICAL - Invalid HTTP response received from host on port 80: cURL returned 28 - Connection timed out after/", "Output OK"); +like( $res->output, "/Invalid HTTP response received from host on port 80: cURL returned 28 - Connection timed out after/", "Output OK"); $res = NPTest->testCmd( "./$plugin $hostname_invalid -wt 1 -ct 2" diff --git a/plugins/tests/check_curl.t b/plugins/tests/check_curl.t index eaa9f518..e66d0ba6 100755 --- a/plugins/tests/check_curl.t +++ b/plugins/tests/check_curl.t @@ -15,6 +15,7 @@ # Email Address []:devel@monitoring-plugins.org use strict; +use warnings; use Test::More; use NPTest; use FindBin qw($Bin); @@ -245,21 +246,21 @@ SKIP: { $result = NPTest->testCmd( "$command -p $port_https -S -C 14" ); is( $result->return_code, 0, "$command -p $port_https -S -C 14" ); - is( $result->output, "OK - Certificate 'Monitoring Plugins' will expire on $expiry.", "output ok" ); + like( $result->output, "/.*Certificate \'Monitoring Plugins\' will expire on $expiry.*", "output ok" ); $result = NPTest->testCmd( "$command -p $port_https -S -C 14000" ); is( $result->return_code, 1, "$command -p $port_https -S -C 14000" ); - like( $result->output, '/WARNING - Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\)./', "output ok" ); + like( $result->output, '/.*Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\).*/', "output ok" ); # Expired cert tests $result = NPTest->testCmd( "$command -p $port_https -S -C 13960,14000" ); is( $result->return_code, 2, "$command -p $port_https -S -C 13960,14000" ); - like( $result->output, '/CRITICAL - Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\)./', "output ok" ); + like( $result->output, '/.*Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\).*/', "output ok" ); $result = NPTest->testCmd( "$command -p $port_https_expired -S -C 7" ); is( $result->return_code, 2, "$command -p $port_https_expired -S -C 7" ); - is( $result->output, - 'CRITICAL - Certificate \'Monitoring Plugins\' expired on Wed Jan 2 12:00:00 2008 +0000.', + like( $result->output, + '/.*Certificate \'Monitoring Plugins\' expired on Wed Jan 2 12:00:00 2008 +0000.*/', "output ok" ); } @@ -274,19 +275,19 @@ SKIP: { $cmd = "./$plugin -H $virtual_host -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host:$port_http\$"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); # http with virtual port (!= 80) $cmd = "./$plugin -H $virtual_host:$virtual_port -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host:$virtual_port\$"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); # http with virtual port (80) $cmd = "./$plugin -H $virtual_host:80 -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host\$"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); } # and the same for SSL @@ -296,19 +297,19 @@ SKIP: { $cmd = "./$plugin -H $virtual_host -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host:$port_https\$"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); # https with virtual port (!= 443) $cmd = "./$plugin -H $virtual_host:$virtual_port -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host:$virtual_port\$"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); # https with virtual port (443) $cmd = "./$plugin -H $virtual_host:443 -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host\$"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); } @@ -321,165 +322,165 @@ sub run_common_tests { $result = NPTest->testCmd( "$command -u /file/root" ); is( $result->return_code, 0, "/file/root"); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second/', "Output correct" ); + like( $result->output, '/.*HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second.*/', "Output correct" ); $result = NPTest->testCmd( "$command -u /file/root -s Root" ); is( $result->return_code, 0, "/file/root search for string"); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second/', "Output correct" ); + like( $result->output, '/.*HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second.*/', "Output correct" ); $result = NPTest->testCmd( "$command -u /file/root -s NonRoot" ); is( $result->return_code, 2, "Missing string check"); - like( $result->output, qr%^HTTP CRITICAL: HTTP/1\.1 200 OK - string 'NonRoot' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location"); + like( $result->output, qr%string 'NonRoot' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location"); $result = NPTest->testCmd( "$command -u /file/root -s NonRootWithOver30charsAndMoreFunThanAWetFish" ); is( $result->return_code, 2, "Missing string check"); - like( $result->output, qr%HTTP CRITICAL: HTTP/1\.1 200 OK - string 'NonRootWithOver30charsAndM...' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location"); + like( $result->output, qr%string 'NonRootWithOver30charsAndM...' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location"); $result = NPTest->testCmd( "$command -u /header_check -d foo" ); is( $result->return_code, 0, "header_check search for string"); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 96 bytes in [\d\.]+ second/', "Output correct" ); + like( $result->output, '/.*HTTP/1.1 200 OK - 96 bytes in [\d\.]+ second.*/', "Output correct" ); $result = NPTest->testCmd( "$command -u /header_check -d bar" ); is( $result->return_code, 2, "Missing header string check"); - like( $result->output, qr%^HTTP CRITICAL: HTTP/1\.1 200 OK - header 'bar' not found on 'https?://127\.0\.0\.1:\d+/header_check'%, "Shows search string and location"); + like( $result->output, qr%header 'bar' not found on 'https?://127\.0\.0\.1:\d+/header_check'%, "Shows search string and location"); $result = NPTest->testCmd( "$command -u /header_broken_check" ); is( $result->return_code, 0, "header_check search for string"); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 138 bytes in [\d\.]+ second/', "Output correct" ); + like( $result->output, '/.*HTTP/1.1 200 OK - 138 bytes in [\d\.]+ second.*/', "Output correct" ); my $cmd; $cmd = "$command -u /slow"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, "$cmd"); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $result->output =~ /in ([\d\.]+) second/; cmp_ok( $1, ">", 1, "Time is > 1 second" ); $cmd = "$command -u /statuscode/200"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -u /statuscode/200 -e 200"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - Status line output matched "200" - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*Status line output matched "200".*/', "Output correct: ".$result->output ); $cmd = "$command -u /statuscode/201"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 201 Created - \d+ bytes in [\d\.]+ second /', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 201 Created - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -u /statuscode/201 -e 201"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 201 Created - Status line output matched "201" - \d+ bytes in [\d\.]+ second /', "Output correct: ".$result->output ); + like( $result->output, '/.*Status line output matched "201".*/', "Output correct: ".$result->output ); $cmd = "$command -u /statuscode/201 -e 200"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 2, $cmd); - like( $result->output, '/^HTTP CRITICAL - Invalid HTTP response received from host on port \d+: HTTP/1.1 201 Created/', "Output correct: ".$result->output ); + like( $result->output, '/.*Invalid HTTP response received from host on port \d+: HTTP/1.1 201 Created.*/', "Output correct: ".$result->output ); $cmd = "$command -u /statuscode/200 -e 200,201,202"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - Status line output matched "200,201,202" - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*Status line output matched "200,201,202".*/', "Output correct: ".$result->output ); $cmd = "$command -u /statuscode/201 -e 200,201,202"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 201 Created - Status line output matched "200,201,202" - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*Status line output matched "200,201,202".*/', "Output correct: ".$result->output ); $cmd = "$command -u /statuscode/203 -e 200,201,202"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 2, $cmd); - like( $result->output, '/^HTTP CRITICAL - Invalid HTTP response received from host on port (\d+): HTTP/1.1 203 Non-Authoritative Information/', "Output correct: ".$result->output ); + like( $result->output, '/.*Invalid HTTP response received from host on port (\d+): HTTP/1.1 203 Non-Authoritative Information.*/', "Output correct: ".$result->output ); $cmd = "$command -j HEAD -u /method"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 HEAD - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 HEAD - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -j POST -u /method"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -j GET -u /method"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -u /method"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -P foo -u /method"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -j DELETE -u /method"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 1, $cmd); - like( $result->output, '/^HTTP WARNING: HTTP/1.1 405 Method Not Allowed/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 405 Method Not Allowed.*/', "Output correct: ".$result->output ); $cmd = "$command -j foo -u /method"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 2, $cmd); - like( $result->output, '/^HTTP CRITICAL: HTTP/1.1 501 Not Implemented/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 501 Not Implemented.*/', "Output correct: ".$result->output ); $cmd = "$command -P stufftoinclude -u /postdata -s POST:stufftoinclude"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -j PUT -P stufftoinclude -u /postdata -s PUT:stufftoinclude"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); # To confirm that the free doesn't segfault $cmd = "$command -P stufftoinclude -j PUT -u /postdata -s PUT:stufftoinclude"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -u /redirect"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -f follow -u /redirect"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -u /redirect -k 'follow: me'"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -f follow -u /redirect -k 'follow: me'"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -f sticky -u /redirect -k 'follow: me'"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -f stickyport -u /redirect -k 'follow: me'"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -f follow -u /redirect_rel -s redirected"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); # These tests may block # stickyport - on full urlS port is set back to 80 otherwise -- cgit v1.2.3-74-g34f1 From def42dc686736107f95d66fe8f35d78528b77779 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 00:22:08 +0200 Subject: Improve error message --- plugins/check_curl.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 89173808..7cb9745f 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -230,9 +230,8 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state /* Curl errors, result in critical Nagios state */ if (res != CURLE_OK) { - xasprintf(&sc_curl.output, - _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"), - workingState.serverPort, res, errbuf[0] ? errbuf : curl_easy_strerror(res)); + xasprintf(&sc_curl.output, _("Error while performing connectiion: cURL returned %d - %s"), + res, errbuf[0] ? errbuf : curl_easy_strerror(res)); sc_curl = mp_set_subcheck_state(sc_curl, STATE_CRITICAL); mp_add_subcheck_to_subcheck(&sc_result, sc_curl); return sc_result; -- cgit v1.2.3-74-g34f1 From 166ce184c67a95192de8ec7f862fd965ea672ba8 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 00:22:35 +0200 Subject: Fix regex matching --- plugins/check_curl.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 7cb9745f..f8adc3d9 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -564,18 +564,24 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state if (errcode == 0) { // got a match if (config.invert_regex) { - sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK); - } else { sc_body_regex = mp_set_subcheck_state(sc_body_regex, config.state_regex); + } else { + sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK); } - } else { + } else if (errcode == REG_NOMATCH) { xasprintf(&sc_body_regex.output, "%s not", sc_body_regex.output); // got no match if (config.invert_regex) { - sc_body_regex = mp_set_subcheck_state(sc_body_regex, config.state_regex); - } else { sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK); + } else { + sc_body_regex = mp_set_subcheck_state(sc_body_regex, config.state_regex); } + } else { + // error in regexec + char error_buffer[DEFAULT_BUFFER_SIZE]; + regerror(errcode, &config.compiled_regex, &error_buffer[0], DEFAULT_BUFFER_SIZE); + xasprintf(&sc_body_regex.output, "regexec error: %s", error_buffer); + sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_UNKNOWN); } mp_add_subcheck_to_subcheck(&sc_result, sc_body_regex); -- cgit v1.2.3-74-g34f1 From 0bf5fdf41e0ce5def14d0c7fda93b57652a9bbb3 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 01:56:46 +0200 Subject: check_curl: finish after cert without continue param --- plugins/check_curl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index f8adc3d9..b6e2e10c 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -273,7 +273,7 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state mp_add_subcheck_to_subcheck(&sc_result, sc_certificate); if (!config.continue_after_check_cert) { - // TODO finish here then + return sc_result; } } #endif -- cgit v1.2.3-74-g34f1 From c15d12cbd5008de74dccd6f716e418feffed5560 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 01:57:08 +0200 Subject: check_curl: fix perfdata label --- plugins/check_curl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index b6e2e10c..680ecef7 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -307,7 +307,7 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state mp_perfdata_value pd_val_time_connect = mp_create_pd_value(time_connect); pd_time_connect.value = pd_val_time_connect; - pd_time_connect.label = "time"; + pd_time_connect.label = "time_connect"; pd_time_connect.uom = "s"; pd_time_connect = mp_set_pd_max_value( pd_time_connect, mp_create_pd_value(config.curl_config.socket_timeout)); -- cgit v1.2.3-74-g34f1 From f5f4a021a2760d553e3e4cdedd291eb815750369 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 01:57:40 +0200 Subject: Add new cert check function --- plugins/check_curl.c | 3 +- plugins/netutils.h | 3 +- plugins/sslutils.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 2 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 680ecef7..b1021045 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -569,8 +569,9 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK); } } else if (errcode == REG_NOMATCH) { - xasprintf(&sc_body_regex.output, "%s not", sc_body_regex.output); // got no match + xasprintf(&sc_body_regex.output, "%s not", sc_body_regex.output); + if (config.invert_regex) { sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK); } else { diff --git a/plugins/netutils.h b/plugins/netutils.h index c53b3cef..6adb8e01 100644 --- a/plugins/netutils.h +++ b/plugins/netutils.h @@ -32,6 +32,7 @@ #define _NETUTILS_H_ #include "common.h" +#include "output.h" #include "states.h" #include "utils.h" #include @@ -114,6 +115,6 @@ void np_net_ssl_cleanup(); int np_net_ssl_write(const void *buf, int num); int np_net_ssl_read(void *buf, int num); mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); +mp_subcheck mp_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); #endif /* HAVE_SSL */ - #endif /* _NETUTILS_H_ */ diff --git a/plugins/sslutils.c b/plugins/sslutils.c index bea1307f..3ce6afed 100644 --- a/plugins/sslutils.c +++ b/plugins/sslutils.c @@ -26,6 +26,7 @@ * *****************************************************************************/ +#include "output.h" #define MAX_CN_LENGTH 256 #include "common.h" #include "netutils.h" @@ -322,4 +323,138 @@ mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_cr # endif /* USE_OPENSSL */ } +mp_subcheck mp_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, + int days_till_exp_crit) { + mp_subcheck sc_cert = mp_subcheck_init(); +# ifdef USE_OPENSSL + if (!certificate) { + xasprintf(&sc_cert.output, _("No server certificate present to inspect")); + sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); + return sc_cert; + } + + /* Extract CN from certificate subject */ + X509_NAME *subj = X509_get_subject_name(certificate); + + if (!subj) { + xasprintf(&sc_cert.output, _("Cannot retrieve certificate subject")); + sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); + return sc_cert; + } + + char commonName[MAX_CN_LENGTH] = ""; + int cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, commonName, sizeof(commonName)); + if (cnlen == -1) { + strcpy(commonName, _("Unknown CN")); + } + + /* Retrieve timestamp of certificate */ + ASN1_STRING *expiry_timestamp = X509_get_notAfter(certificate); + + int offset = 0; + struct tm stamp = {}; + /* Generate tm structure to process timestamp */ + if (expiry_timestamp->type == V_ASN1_UTCTIME) { + if (expiry_timestamp->length < 10) { + xasprintf(&sc_cert.output, _("Wrong time format in certificate")); + sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); + return sc_cert; + } + + stamp.tm_year = (expiry_timestamp->data[0] - '0') * 10 + (expiry_timestamp->data[1] - '0'); + if (stamp.tm_year < 50) { + stamp.tm_year += 100; + } + + offset = 0; + } else { + if (expiry_timestamp->length < 12) { + xasprintf(&sc_cert.output, _("Wrong time format in certificate")); + sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); + return sc_cert; + } + stamp.tm_year = (expiry_timestamp->data[0] - '0') * 1000 + + (expiry_timestamp->data[1] - '0') * 100 + + (expiry_timestamp->data[2] - '0') * 10 + (expiry_timestamp->data[3] - '0'); + stamp.tm_year -= 1900; + offset = 2; + } + + stamp.tm_mon = (expiry_timestamp->data[2 + offset] - '0') * 10 + + (expiry_timestamp->data[3 + offset] - '0') - 1; + stamp.tm_mday = (expiry_timestamp->data[4 + offset] - '0') * 10 + + (expiry_timestamp->data[5 + offset] - '0'); + stamp.tm_hour = (expiry_timestamp->data[6 + offset] - '0') * 10 + + (expiry_timestamp->data[7 + offset] - '0'); + stamp.tm_min = (expiry_timestamp->data[8 + offset] - '0') * 10 + + (expiry_timestamp->data[9 + offset] - '0'); + stamp.tm_sec = (expiry_timestamp->data[10 + offset] - '0') * 10 + + (expiry_timestamp->data[11 + offset] - '0'); + stamp.tm_isdst = -1; + + time_t tm_t = timegm(&stamp); + double time_left = difftime(tm_t, time(NULL)); + int days_left = (int)(time_left / 86400); + char *timeZone = getenv("TZ"); + setenv("TZ", "GMT", 1); + tzset(); + + char timestamp[50] = ""; + strftime(timestamp, 50, "%c %z", localtime(&tm_t)); + if (timeZone) { + setenv("TZ", timeZone, 1); + } else { + unsetenv("TZ"); + } + + tzset(); + + int time_remaining; + if (days_left > 0 && days_left <= days_till_exp_warn) { + xasprintf(&sc_cert.output, _("Certificate '%s' expires in %d day(s) (%s)"), commonName, + days_left, timestamp); + if (days_left > days_till_exp_crit) { + sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING); + } else { + sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); + } + } else if (days_left == 0 && time_left > 0) { + if (time_left >= 3600) { + time_remaining = (int)time_left / 3600; + } else { + time_remaining = (int)time_left / 60; + } + + xasprintf(&sc_cert.output, _("Certificate '%s' expires in %u %s (%s)"), commonName, + time_remaining, time_left >= 3600 ? "hours" : "minutes", timestamp); + + if (days_left > days_till_exp_crit) { + sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING); + } else { + sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); + } + } else if (time_left < 0) { + xasprintf(&sc_cert.output, _("Certificate '%s' expired on %s"), commonName, timestamp); + sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); + } else if (days_left == 0) { + xasprintf(&sc_cert.output, _("Certificate '%s' just expired (%s)"), commonName, + timestamp); + if (days_left > days_till_exp_crit) { + sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING); + } else { + sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); + } + } else { + xasprintf(&sc_cert.output, _("Certificate '%s' will expire on %s"), commonName, + timestamp); + sc_cert = mp_set_subcheck_state(sc_cert, STATE_OK); + } + X509_free(certificate); + return sc_cert; +# else /* ifndef USE_OPENSSL */ + xasprintf(&sc_cert.output, _("Plugin does not support checking certificates")); + sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING); + return sc_cert; +# endif /* USE_OPENSSL */ +} #endif /* HAVE_SSL */ -- cgit v1.2.3-74-g34f1 From 43c6332322f47285576218429be6a41c03912b57 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 02:08:46 +0200 Subject: Fix typo in error message --- plugins/check_curl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index b1021045..6d50568f 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -230,7 +230,7 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state /* Curl errors, result in critical Nagios state */ if (res != CURLE_OK) { - xasprintf(&sc_curl.output, _("Error while performing connectiion: cURL returned %d - %s"), + xasprintf(&sc_curl.output, _("Error while performing connection: cURL returned %d - %s"), res, errbuf[0] ? errbuf : curl_easy_strerror(res)); sc_curl = mp_set_subcheck_state(sc_curl, STATE_CRITICAL); mp_add_subcheck_to_subcheck(&sc_result, sc_curl); -- cgit v1.2.3-74-g34f1 From 5a2c1b2c3aeb99e7a703b0c5f6fe1a21d29b3be4 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 02:30:42 +0200 Subject: Add output formatting option --- plugins/check_curl.c | 22 +++++++++++++++++++++- plugins/check_curl.d/check_curl_helpers.c | 2 ++ plugins/check_curl.d/config.h | 3 +++ 3 files changed, 26 insertions(+), 1 deletion(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 6d50568f..fc704171 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -139,6 +139,10 @@ int main(int argc, char **argv) { const check_curl_config config = tmp_config.config; + if (config.output_format_is_set) { + mp_set_format(config.output_format); + } + check_curl_working_state working_state = config.initial_config; mp_check overall = mp_check_init(); @@ -828,7 +832,8 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { AUTOMATIC_DECOMPRESSION, COOKIE_JAR, HAPROXY_PROTOCOL, - STATE_REGEX + STATE_REGEX, + OUTPUT_FORMAT }; static struct option longopts[] = { @@ -875,6 +880,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { {"enable-automatic-decompression", no_argument, 0, AUTOMATIC_DECOMPRESSION}, {"cookie-jar", required_argument, 0, COOKIE_JAR}, {"haproxy-protocol", no_argument, 0, HAPROXY_PROTOCOL}, + {"output-format", required_argument, 0, OUTPUT_FORMAT}, {0, 0, 0, 0}}; check_curl_config_wrapper result = { @@ -1301,6 +1307,18 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { /* print short usage statement if args not parsable */ usage5(); break; + case OUTPUT_FORMAT: { + parsed_output_format parser = mp_parse_output_format(optarg); + if (!parser.parsing_success) { + // TODO List all available formats here, maybe add anothoer usage function + printf("Invalid output format: %s\n", optarg); + exit(STATE_UNKNOWN); + } + + result.config.output_format_is_set = true; + result.config.output_format = parser.output_format; + break; + } } } @@ -1602,6 +1620,8 @@ void print_help(void) { printf(UT_VERBOSE); + printf(UT_OUTPUT_FORMAT); + printf("\n"); printf("%s\n", _("Notes:")); printf(" %s\n", _("This plugin will attempt to open an HTTP connection with the host.")); diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c index ab43a1e1..c3c2ba55 100644 --- a/plugins/check_curl.d/check_curl_helpers.c +++ b/plugins/check_curl.d/check_curl_helpers.c @@ -632,6 +632,8 @@ check_curl_config check_curl_config_init() { .show_extended_perfdata = false, .show_body = false, + + .output_format_is_set = false, }; snprintf(tmp.curl_config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index 6100af3e..f51b2ee9 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -108,6 +108,9 @@ typedef struct { bool show_extended_perfdata; bool show_body; + + bool output_format_is_set; + mp_output_format output_format; } check_curl_config; check_curl_config check_curl_config_init(); -- cgit v1.2.3-74-g34f1 From 2917b8735f1d56211eac0ad1bf7a051a842abd76 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 27 Nov 2025 13:24:30 +0100 Subject: check_curl: abort redir if location is not found This commit changes the behaviour of check_curl slightly. Previously when the redirection method was set to the old 'check_http' style redirection and there was no "location" header in the original answer 'check_curl' segfaulted. Now, at least it dies properly with a message. --- plugins/check_curl.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index fc704171..4eaa5f1d 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -672,6 +672,11 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config char *location = get_header_value(headers, nof_headers, "location"); + if (location == NULL) { + // location header not found + die(STATE_UNKNOWN, "HTTP UNKNOWN - could not find \"location\" header\n"); + } + if (verbose >= 2) { printf(_("* Seen redirect location %s\n"), location); } -- cgit v1.2.3-74-g34f1 From 9fc1e24543dc0b79a8f75f078feced6c5ee1fa96 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 27 Nov 2025 14:29:33 +0100 Subject: check_curl: try to be more helpful in check_curls help Trying to be more specific with the check_curl help. The idea is to clarify how the parameters are supposed to be used. --- plugins/check_curl.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index fc704171..2c258f36 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -1480,7 +1480,9 @@ void print_help(void) { printf(" %s\n", _("Append a port to include it in the header (eg: example.com:5000)")); printf(" %s\n", "-I, --IP-address=ADDRESS"); printf(" %s\n", - _("IP address or name (use numeric address if possible to bypass DNS lookup).")); + "IP address or name (use numeric address if possible to bypass DNS lookup).\n"); + printf(" %s\n", + "This overwrites the network address of the target while leaving everything else (HTTP headers) as they are"); printf(" %s\n", "-p, --port=INTEGER"); printf(" %s", _("Port number (default: ")); printf("%d)\n", HTTP_PORT); @@ -1544,6 +1546,7 @@ void print_help(void) { printf(" %s\n", _("String to expect in the content")); printf(" %s\n", "-u, --url=PATH"); printf(" %s\n", _("URL to GET or POST (default: /)")); + printf(" %s\n", _("This is the part after the address in a URL, so for \"https://example.com/index.html\" it would be '-u /index.html'")); printf(" %s\n", "-P, --post=STRING"); printf(" %s\n", _("URL decoded http POST data")); printf(" %s\n", @@ -1685,7 +1688,7 @@ void print_help(void) { printf(" %s\n", _("It is recommended to use an environment proxy like:")); printf(" %s\n", _("https_proxy=http://192.168.100.35:3128 ./check_curl -H www.verisign.com -S")); - printf(" %s\n", _("legacy proxy requests in check_http style still work:")); + printf(" %s\n", _("legacy proxy requests in check_http style might still work, but are frowned upon, so DONT:")); printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u https://www.verisign.com/ -S -j " "CONNECT -H www.verisign.com ")); printf(" %s\n", _("all these options are needed: -I -p -u " -- cgit v1.2.3-74-g34f1 From 57043387ea640ecf0a32fded0974f6366a53766f Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 27 Nov 2025 14:42:05 +0100 Subject: Remove some superfluous newlines --- plugins/check_curl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 2c258f36..a20ce749 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -1480,7 +1480,7 @@ void print_help(void) { printf(" %s\n", _("Append a port to include it in the header (eg: example.com:5000)")); printf(" %s\n", "-I, --IP-address=ADDRESS"); printf(" %s\n", - "IP address or name (use numeric address if possible to bypass DNS lookup).\n"); + "IP address or name (use numeric address if possible to bypass DNS lookup)."); printf(" %s\n", "This overwrites the network address of the target while leaving everything else (HTTP headers) as they are"); printf(" %s\n", "-p, --port=INTEGER"); @@ -1559,7 +1559,7 @@ void print_help(void) { printf(" %s\n", _("Warn if document is more than SECONDS old. the number can also be of")); printf(" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days.")); printf(" %s\n", "-T, --content-type=STRING"); - printf(" %s\n", _("specify Content-Type header media type when POSTing\n")); + printf(" %s\n", _("specify Content-Type header media type when POSTing")); printf(" %s\n", "-l, --linespan"); printf(" %s\n", _("Allow regex to span newlines (must precede -r or -R)")); printf(" %s\n", "-r, --regex, --ereg=STRING"); -- cgit v1.2.3-74-g34f1 From db2983da7e8175ca3928998d4547acdf75d55dc0 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 28 Nov 2025 12:21:08 +0100 Subject: Fix/check curl sticky redir (#2188) * check_curl: avoid freeing memory when we don't know where it came from * check_curl: when using -f sticky conserve IPv6 addresses properly When running the check on an ipv6 address with a sticky onredirect policy like in this example: check_curl -6 -H example.com -I ::1 -f sticky It results in a getaddrinfo error: HTTP CRITICAL - Unable to lookup IP address for '[::1]': getaddrinfo returned -3 - Temporary failure in name resolution This happens because in check_http() if the content of server_addr is an ipv6 address enclosing brackets are added and on redirection a subsequent call to check_http() will pass this now bracketed value to getaddrinfo resulting in the error. To work around this, strip the brackets from the address prior to the lookup_host() call. * add Michael Jeanson to thanks --- THANKS.in | 1 + plugins/check_curl.c | 10 +++------- plugins/check_curl.d/check_curl_helpers.c | 14 +++++++++++++- plugins/check_curl.d/check_curl_helpers.h | 4 ++++ 4 files changed, 21 insertions(+), 8 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/THANKS.in b/THANKS.in index 66397ad1..5dbb1b39 100644 --- a/THANKS.in +++ b/THANKS.in @@ -426,3 +426,4 @@ Eunice Remoquillo Louis Sautier Sven Hartge Alvar Penning +Michael Jeanson diff --git a/plugins/check_curl.c b/plugins/check_curl.c index ba856a11..e3e514ff 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -68,10 +68,6 @@ const char *email = "devel@monitoring-plugins.org"; #include -enum { - MAX_IPV4_HOSTLENGTH = 255, -}; - enum { REGS = 2, }; @@ -789,21 +785,21 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config /* set new values for redirected request */ if (!(config.followsticky & STICKY_HOST)) { - free(working_state.server_address); + // free(working_state.server_address); working_state.server_address = strndup(new_host, MAX_IPV4_HOSTLENGTH); } if (!(config.followsticky & STICKY_PORT)) { working_state.serverPort = (unsigned short)new_port; } - free(working_state.host_name); + // free(working_state.host_name); working_state.host_name = strndup(new_host, MAX_IPV4_HOSTLENGTH); /* reset virtual port */ working_state.virtualPort = working_state.serverPort; free(new_host); - free(working_state.server_url); + // free(working_state.server_url); working_state.server_url = new_url; uriFreeUriMembersA(&uri); diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c index d49d8f07..e4e7bef6 100644 --- a/plugins/check_curl.d/check_curl_helpers.c +++ b/plugins/check_curl.d/check_curl_helpers.c @@ -128,8 +128,20 @@ check_curl_configure_curl(const check_curl_static_curl_config config, char dnscache[DEFAULT_BUFFER_SIZE]; char addrstr[DEFAULT_BUFFER_SIZE / 2]; if (working_state.use_ssl && working_state.host_name != NULL) { + char *tmp_mod_address; + + /* lookup_host() requires an IPv6 address without the brackets. */ + if ((strnlen(working_state.server_address, MAX_IPV4_HOSTLENGTH) > 2) && + (working_state.server_address[0] == '[')) { + // Duplicate and strip the leading '[' + tmp_mod_address = + strndup(working_state.server_address + 1, strlen(working_state.server_address) - 2); + } else { + tmp_mod_address = working_state.server_address; + } + int res; - if ((res = lookup_host(working_state.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2, + if ((res = lookup_host(tmp_mod_address, addrstr, DEFAULT_BUFFER_SIZE / 2, config.sin_family)) != 0) { die(STATE_CRITICAL, _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), diff --git a/plugins/check_curl.d/check_curl_helpers.h b/plugins/check_curl.d/check_curl_helpers.h index 87e45a9d..e7b80f7e 100644 --- a/plugins/check_curl.d/check_curl_helpers.h +++ b/plugins/check_curl.d/check_curl_helpers.h @@ -7,6 +7,10 @@ # include #endif +enum { + MAX_IPV4_HOSTLENGTH = 255, +}; + /* for buffers for header and body */ typedef struct { size_t buflen; -- cgit v1.2.3-74-g34f1 From e0b30cc6e82113491261c872a371a2fa0b4db3ba Mon Sep 17 00:00:00 2001 From: Ahmet Oeztuerk Date: Thu, 4 Dec 2025 16:52:55 +0100 Subject: append the query string from parsed uri Check the UriUriA object, and if query string exists append it to the new_url. Only appends the query part, fragments are still not appended Function redir parses the new location header value using the uriParseUriA function already, which populates the query field. This field was already being printed, but it was not being appended to the new_url during its construction. Redirection chain of check_curl --onredirect=follow now mimics the chain of check_http --onredirect=follow. Tested on the url: mail.google.com/chat --- plugins/check_curl.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index e3e514ff..ca6357a7 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -761,7 +761,7 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config } /* compose new path */ - /* TODO: handle fragments and query part of URL */ + /* TODO: handle fragments of URL */ char *new_url = (char *)calloc(1, DEFAULT_BUFFER_SIZE); if (uri.pathHead) { for (UriPathSegmentA *pathSegment = uri.pathHead; pathSegment; @@ -772,6 +772,25 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config } } + /* missing components have null,null in their UriTextRangeA + * add query parameters if they exist. + */ + if (uri.query.first && uri.query.afterLast){ + // Ensure we have space for '?' + query_str + '\0' ahead of time, instead of calling strncat twice + size_t current_len = strlen(new_url); + size_t remaining_space = DEFAULT_BUFFER_SIZE - current_len - 1; + + const char* query_str = uri_string(uri.query, buf, DEFAULT_BUFFER_SIZE); + size_t query_str_len = strlen(query_str); + + if (remaining_space >= query_str_len + 1) { + strcat(new_url, "?"); + strcat(new_url, query_str); + }else{ + die(STATE_UNKNOWN, _("HTTP UNKNOWN - No space to add query part of size %d to the buffer, buffer has remaining size %d"), query_str_len , current_len ); + } + } + if (working_state.serverPort == new_port && !strncmp(working_state.server_address, new_host, MAX_IPV4_HOSTLENGTH) && (working_state.host_name && -- cgit v1.2.3-74-g34f1 From eed2dd00bb61db0f7654e394948a42b8bf0b14f3 Mon Sep 17 00:00:00 2001 From: Alvar Date: Sun, 14 Dec 2025 11:53:51 +0000 Subject: check_curl: --verify-cert / -D in examples (#2204) Include the -D flag for certificate verification in the "CHECK CERTIFICATE" examples. Otherwise, only the certificate dates are checked, but not if the certificate matches to the hostname or is signed by a trusted CA. Fixes #2146. --- plugins/check_curl.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index e3e514ff..0aff8b40 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -1648,6 +1648,8 @@ void print_help(void) { printf(" %s\n", _("certificate matches the hostname of the server, or if the certificate")); printf(" %s\n", _("has a valid chain of trust to one of the locally installed CAs.")); printf("\n"); + printf(" %s\n", _("To also verify certificates, please set --verify-cert.")); + printf("\n"); printf("%s\n", _("Examples:")); printf(" %s\n\n", "CHECK CONTENT: check_curl -w 5 -c 10 --ssl -H www.verisign.com"); printf(" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,")); @@ -1657,16 +1659,18 @@ void print_help(void) { _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); printf(" %s\n", _("a STATE_CRITICAL will be returned.")); printf("\n"); - printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 14"); + printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 14 -D"); printf(" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,")); printf(" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than")); printf(" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when")); - printf(" %s\n\n", _("the certificate is expired.")); + printf(" %s\n", _("the certificate is expired.")); + printf("\n"); + printf(" %s\n", _("The -D flag enforces a certificate validation beyond expiration time.")); printf("\n"); - printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 30,14"); + printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 30,14 -D"); printf(" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 30 days,")); printf(" %s\n", -- cgit v1.2.3-74-g34f1 From d36bf51baf2338cc7f49601f679548960a8299c0 Mon Sep 17 00:00:00 2001 From: Stuart Henderson Date: Tue, 23 Dec 2025 15:54:27 +0000 Subject: fix types for most curl_easy_setopt parameters according to https://curl.se/libcurl/c/curl_easy_setopt.html, parameters are either a long, a function pointer, an object pointer or a curl_off_t, depending on what the option expects; curl 8.16 checks and warns about these. --- plugins/check_curl.c | 20 ++++++++++---------- plugins/check_curl.d/check_curl_helpers.c | 30 ++++++++++++++++-------------- plugins/check_curl.d/check_curl_helpers.h | 2 +- plugins/check_curl.d/config.h | 6 +++--- 4 files changed, 30 insertions(+), 28 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 0aff8b40..a1fefa3a 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -92,16 +92,16 @@ typedef struct { static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); static mp_subcheck check_http(check_curl_config /*config*/, check_curl_working_state workingState, - int redir_depth); + long redir_depth); typedef struct { - int redir_depth; + long redir_depth; check_curl_working_state working_state; int error_code; check_curl_global_state curl_state; } redir_wrapper; static redir_wrapper redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/, - int redir_depth, check_curl_working_state working_state); + long redir_depth, check_curl_working_state working_state); static void print_help(void); void print_usage(void); @@ -198,7 +198,7 @@ CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) { #endif /* HAVE_SSL */ mp_subcheck check_http(const check_curl_config config, check_curl_working_state workingState, - int redir_depth) { + long redir_depth) { // ======================= // Initialisation for curl @@ -441,19 +441,19 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state "CURLINFO_REDIRECT_COUNT"); if (verbose >= 2) { - printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth); + printf(_("* curl LIBINFO_REDIRECT_COUNT is %ld\n"), redir_depth); } mp_subcheck sc_redir_depth = mp_subcheck_init(); if (redir_depth > config.max_depth) { xasprintf(&sc_redir_depth.output, - "maximum redirection depth %d exceeded in libcurl", + "maximum redirection depth %ld exceeded in libcurl", config.max_depth); sc_redir_depth = mp_set_subcheck_state(sc_redir_depth, STATE_CRITICAL); mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth); return sc_result; } - xasprintf(&sc_redir_depth.output, "redirection depth %d (of a maximum %d)", + xasprintf(&sc_redir_depth.output, "redirection depth %ld (of a maximum %ld)", redir_depth, config.max_depth); mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth); @@ -653,7 +653,7 @@ char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) { } redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config config, - int redir_depth, check_curl_working_state working_state) { + long redir_depth, check_curl_working_state working_state) { curlhelp_statusline status_line; struct phr_header headers[255]; size_t msglen; @@ -678,7 +678,7 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config } if (++redir_depth > config.max_depth) { - die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s\n"), + die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %ld exceeded - %s\n"), config.max_depth, location); } @@ -1400,7 +1400,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */ if (verbose >= 2) { - printf(_("* Set SSL/TLS version to %d\n"), result.config.curl_config.ssl_version); + printf(_("* Set SSL/TLS version to %ld\n"), result.config.curl_config.ssl_version); } if (!specify_port) { result.config.initial_config.serverPort = HTTPS_PORT; diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c index e4e7bef6..7be781fc 100644 --- a/plugins/check_curl.d/check_curl_helpers.c +++ b/plugins/check_curl.d/check_curl_helpers.c @@ -19,7 +19,7 @@ bool add_sslctx_verify_fun = false; check_curl_configure_curl_wrapper check_curl_configure_curl(const check_curl_static_curl_config config, check_curl_working_state working_state, bool check_cert, - bool on_redirect_dependent, int follow_method, int max_depth) { + bool on_redirect_dependent, int follow_method, long max_depth) { check_curl_configure_curl_wrapper result = { .errorcode = OK, .curl_state = @@ -57,7 +57,7 @@ check_curl_configure_curl(const check_curl_static_curl_config config, result.curl_state.curl_easy_initialized = true; if (verbose >= 1) { - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_VERBOSE, 1), + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_VERBOSE, 1L), "CURLOPT_VERBOSE"); } @@ -214,10 +214,10 @@ check_curl_configure_curl(const check_curl_static_curl_config config, if (working_state.http_method) { if (!strcmp(working_state.http_method, "POST")) { handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_POST, 1), "CURLOPT_POST"); + curl_easy_setopt(result.curl_state.curl, CURLOPT_POST, 1L), "CURLOPT_POST"); } else if (!strcmp(working_state.http_method, "PUT")) { handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD"); + curl_easy_setopt(result.curl_state.curl, CURLOPT_UPLOAD, 1L), "CURLOPT_UPLOAD"); } else { handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_CUSTOMREQUEST, @@ -300,10 +300,10 @@ check_curl_configure_curl(const check_curl_static_curl_config config, /* per default if we have a CA verify both the peer and the * hostname in the certificate, can be switched off later */ handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 1), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 1L), "CURLOPT_SSL_VERIFYPEER"); handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 2), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 2L), "CURLOPT_SSL_VERIFYHOST"); } else { /* backward-compatible behaviour, be tolerant in checks @@ -311,10 +311,10 @@ check_curl_configure_curl(const check_curl_static_curl_config config, * to be less tolerant about ssl verfications */ handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 0), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 0L), "CURLOPT_SSL_VERIFYPEER"); handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 0), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 0L), "CURLOPT_SSL_VERIFYHOST"); } @@ -438,7 +438,7 @@ check_curl_configure_curl(const check_curl_static_curl_config config, if (on_redirect_dependent) { if (follow_method == FOLLOW_LIBCURL) { handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_FOLLOWLOCATION, 1), + curl_easy_setopt(result.curl_state.curl, CURLOPT_FOLLOWLOCATION, 1L), "CURLOPT_FOLLOWLOCATION"); /* default -1 is infinite, not good, could lead to zombie plugins! @@ -474,7 +474,7 @@ check_curl_configure_curl(const check_curl_static_curl_config config, } /* no-body */ if (working_state.no_body) { - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_NOBODY, 1), + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_NOBODY, 1L), "CURLOPT_NOBODY"); } @@ -796,15 +796,17 @@ mp_subcheck check_document_dates(const curlhelp_write_curlbuf *header_buf, const ((float)last_modified) / (60 * 60 * 24)); sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); } else { - xasprintf(&sc_document_dates.output, _("Last modified %ld:%02ld:%02ld ago"), - last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60); + xasprintf(&sc_document_dates.output, _("Last modified %lld:%02d:%02d ago"), + (long long)last_modified / (60 * 60), (int)(last_modified / 60) % 60, + (int)last_modified % 60); sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); } } else { // TODO is this the OK case? time_t last_modified = (srv_data - doc_data); - xasprintf(&sc_document_dates.output, _("Last modified %ld:%02ld:%02ld ago"), - last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60); + xasprintf(&sc_document_dates.output, _("Last modified %lld:%02d:%02d ago"), + (long long)last_modified / (60 * 60), (int)(last_modified / 60) % 60, + (int)last_modified % 60); sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_OK); } } diff --git a/plugins/check_curl.d/check_curl_helpers.h b/plugins/check_curl.d/check_curl_helpers.h index e7b80f7e..e77b763b 100644 --- a/plugins/check_curl.d/check_curl_helpers.h +++ b/plugins/check_curl.d/check_curl_helpers.h @@ -80,7 +80,7 @@ check_curl_configure_curl_wrapper check_curl_configure_curl(check_curl_static_cu check_curl_working_state working_state, bool check_cert, bool on_redirect_dependent, - int follow_method, int max_depth); + int follow_method, long max_depth); void handle_curl_option_return_code(CURLcode res, const char *option); diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index f51b2ee9..61067d46 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -57,10 +57,10 @@ typedef struct { bool haproxy_protocol; long socket_timeout; sa_family_t sin_family; - int curl_http_version; + long curl_http_version; char **http_opt_headers; size_t http_opt_headers_count; - int ssl_version; + long ssl_version; char *client_cert; char *client_privkey; char *ca_cert; @@ -76,7 +76,7 @@ typedef struct { check_curl_working_state initial_config; check_curl_static_curl_config curl_config; - int max_depth; + long max_depth; int followmethod; int followsticky; -- cgit v1.2.3-74-g34f1 From 544ea5bf954abd3ba6bb1b3ec277539dd53c94b9 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 9 Jan 2026 13:09:46 +0100 Subject: Fix error message formatting --- plugins/check_curl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index e7737c7c..0e84b96c 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -787,7 +787,7 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config strcat(new_url, "?"); strcat(new_url, query_str); }else{ - die(STATE_UNKNOWN, _("HTTP UNKNOWN - No space to add query part of size %d to the buffer, buffer has remaining size %d"), query_str_len , current_len ); + die(STATE_UNKNOWN, _("HTTP UNKNOWN - No space to add query part of size %zu to the buffer, buffer has remaining size %zu"), query_str_len , current_len ); } } -- cgit v1.2.3-74-g34f1 From bbc0c8b29fdbefead8b49bc0bd77193f6fc83a1a Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 9 Jan 2026 13:10:23 +0100 Subject: check_curl: clang-format --- plugins/check_curl.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'plugins/check_curl.c') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 0e84b96c..1dec8a2a 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -775,19 +775,23 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config /* missing components have null,null in their UriTextRangeA * add query parameters if they exist. */ - if (uri.query.first && uri.query.afterLast){ - // Ensure we have space for '?' + query_str + '\0' ahead of time, instead of calling strncat twice + if (uri.query.first && uri.query.afterLast) { + // Ensure we have space for '?' + query_str + '\0' ahead of time, instead of calling strncat + // twice size_t current_len = strlen(new_url); size_t remaining_space = DEFAULT_BUFFER_SIZE - current_len - 1; - const char* query_str = uri_string(uri.query, buf, DEFAULT_BUFFER_SIZE); + const char *query_str = uri_string(uri.query, buf, DEFAULT_BUFFER_SIZE); size_t query_str_len = strlen(query_str); if (remaining_space >= query_str_len + 1) { strcat(new_url, "?"); strcat(new_url, query_str); - }else{ - die(STATE_UNKNOWN, _("HTTP UNKNOWN - No space to add query part of size %zu to the buffer, buffer has remaining size %zu"), query_str_len , current_len ); + } else { + die(STATE_UNKNOWN, + _("HTTP UNKNOWN - No space to add query part of size %zu to the buffer, buffer has " + "remaining size %zu"), + query_str_len, current_len); } } @@ -1501,8 +1505,8 @@ void print_help(void) { printf(" %s\n", "-I, --IP-address=ADDRESS"); printf(" %s\n", "IP address or name (use numeric address if possible to bypass DNS lookup)."); - printf(" %s\n", - "This overwrites the network address of the target while leaving everything else (HTTP headers) as they are"); + printf(" %s\n", "This overwrites the network address of the target while leaving everything " + "else (HTTP headers) as they are"); printf(" %s\n", "-p, --port=INTEGER"); printf(" %s", _("Port number (default: ")); printf("%d)\n", HTTP_PORT); @@ -1566,7 +1570,8 @@ void print_help(void) { printf(" %s\n", _("String to expect in the content")); printf(" %s\n", "-u, --url=PATH"); printf(" %s\n", _("URL to GET or POST (default: /)")); - printf(" %s\n", _("This is the part after the address in a URL, so for \"https://example.com/index.html\" it would be '-u /index.html'")); + printf(" %s\n", _("This is the part after the address in a URL, so for " + "\"https://example.com/index.html\" it would be '-u /index.html'")); printf(" %s\n", "-P, --post=STRING"); printf(" %s\n", _("URL decoded http POST data")); printf(" %s\n", @@ -1712,7 +1717,8 @@ void print_help(void) { printf(" %s\n", _("It is recommended to use an environment proxy like:")); printf(" %s\n", _("https_proxy=http://192.168.100.35:3128 ./check_curl -H www.verisign.com -S")); - printf(" %s\n", _("legacy proxy requests in check_http style might still work, but are frowned upon, so DONT:")); + printf(" %s\n", _("legacy proxy requests in check_http style might still work, but are frowned " + "upon, so DONT:")); printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u https://www.verisign.com/ -S -j " "CONNECT -H www.verisign.com ")); printf(" %s\n", _("all these options are needed: -I -p -u " -- cgit v1.2.3-74-g34f1