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/check_curl.d/check_curl_helpers.h | 125 ++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 plugins/check_curl.d/check_curl_helpers.h (limited to 'plugins/check_curl.d/check_curl_helpers.h') 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); -- 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.d/check_curl_helpers.h') 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