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