summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLorenz Kästle <12514511+RincewindsHat@users.noreply.github.com>2025-09-12 16:37:24 +0200
committerLorenz Kästle <12514511+RincewindsHat@users.noreply.github.com>2025-09-12 16:37:24 +0200
commit44b2a25a6b9fb0791ec7150100a4d51e5f129611 (patch)
tree4fb13f9effac32bf156cc6eaefe22f6deea6d7ea
parentaaff3aa9da27ff7666d2a776d524c784b76eb3d7 (diff)
downloadmonitoring-plugins-44b2a25a6b9fb0791ec7150100a4d51e5f129611.tar.gz
check_curl: implement new output mechanism
-rw-r--r--plugins/check_curl.c562
-rw-r--r--plugins/check_curl.d/check_curl_helpers.c251
-rw-r--r--plugins/check_curl.d/check_curl_helpers.h21
-rw-r--r--plugins/check_curl.d/config.h7
4 files changed, 443 insertions, 398 deletions
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";
42#include "thresholds.h" 42#include "thresholds.h"
43#include <stdbool.h> 43#include <stdbool.h>
44#include <ctype.h> 44#include <ctype.h>
45#include "output.h"
46#include "perfdata.h"
45 47
48#include <assert.h>
46#include "common.h" 49#include "common.h"
47#include "utils.h" 50#include "utils.h"
48#include "./check_curl.d/check_curl_helpers.h" 51#include "./check_curl.d/check_curl_helpers.h"
@@ -78,7 +81,6 @@ enum {
78// Globals 81// Globals
79int verbose = 0; 82int verbose = 0;
80 83
81extern char msg[DEFAULT_BUFFER_SIZE];
82extern char errbuf[MAX_INPUT_BUFFER]; 84extern char errbuf[MAX_INPUT_BUFFER];
83extern bool is_openssl_callback; 85extern bool is_openssl_callback;
84extern bool add_sslctx_verify_fun; 86extern bool add_sslctx_verify_fun;
@@ -93,8 +95,8 @@ typedef struct {
93} check_curl_config_wrapper; 95} check_curl_config_wrapper;
94static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); 96static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
95 97
96static mp_state_enum check_http(check_curl_config /*config*/, check_curl_working_state workingState, 98static mp_subcheck check_http(check_curl_config /*config*/, check_curl_working_state workingState,
97 int redir_depth); 99 int redir_depth);
98 100
99typedef struct { 101typedef struct {
100 int redir_depth; 102 int redir_depth;
@@ -149,7 +151,12 @@ int main(int argc, char **argv) {
149 151
150 check_curl_working_state working_state = config.initial_config; 152 check_curl_working_state working_state = config.initial_config;
151 153
152 exit((int)check_http(config, working_state, 0)); 154 mp_check overall = mp_check_init();
155 mp_subcheck sc_test = check_http(config, working_state, 0);
156
157 mp_add_subcheck_to_check(&overall, sc_test);
158
159 mp_exit(overall);
153} 160}
154 161
155#ifdef HAVE_SSL 162#ifdef HAVE_SSL
@@ -200,8 +207,8 @@ CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) {
200# endif /* USE_OPENSSL */ 207# endif /* USE_OPENSSL */
201#endif /* HAVE_SSL */ 208#endif /* HAVE_SSL */
202 209
203mp_state_enum check_http(const check_curl_config config, check_curl_working_state workingState, 210mp_subcheck check_http(const check_curl_config config, check_curl_working_state workingState,
204 int redir_depth) { 211 int redir_depth) {
205 212
206 // ======================= 213 // =======================
207 // Initialisation for curl 214 // Initialisation for curl
@@ -213,6 +220,13 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat
213 check_curl_global_state curl_state = conf_curl_struct.curl_state; 220 check_curl_global_state curl_state = conf_curl_struct.curl_state;
214 workingState = conf_curl_struct.working_state; 221 workingState = conf_curl_struct.working_state;
215 222
223 mp_subcheck sc_result = mp_subcheck_init();
224
225 char *url = fmt_url(workingState);
226 xasprintf(&sc_result.output, "Testing %s", url);
227 // TODO add some output here URL or something
228 free(url);
229
216 // ============== 230 // ==============
217 // do the request 231 // do the request
218 // ============== 232 // ==============
@@ -222,172 +236,149 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat
222 printf("**** REQUEST CONTENT ****\n%s\n", workingState.http_post_data); 236 printf("**** REQUEST CONTENT ****\n%s\n", workingState.http_post_data);
223 } 237 }
224 238
239 mp_subcheck sc_curl = mp_subcheck_init();
240
225 /* Curl errors, result in critical Nagios state */ 241 /* Curl errors, result in critical Nagios state */
226 if (res != CURLE_OK) { 242 if (res != CURLE_OK) {
227 snprintf(msg, DEFAULT_BUFFER_SIZE, 243 xasprintf(&sc_curl.output,
228 _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"), 244 _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"),
229 workingState.serverPort, res, errbuf[0] ? errbuf : curl_easy_strerror(res)); 245 workingState.serverPort, res, errbuf[0] ? errbuf : curl_easy_strerror(res));
230 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); 246 sc_curl = mp_set_subcheck_state(sc_curl, STATE_CRITICAL);
247 mp_add_subcheck_to_subcheck(&sc_result, sc_curl);
248 return sc_result;
231 } 249 }
232 250
251 xasprintf(&sc_curl.output, "cURL performed query");
252 sc_curl = mp_set_subcheck_state(sc_curl, STATE_OK);
253 mp_add_subcheck_to_subcheck(&sc_result, sc_curl);
254
233 // ========== 255 // ==========
234 // Evaluation 256 // Evaluation
235 // ========== 257 // ==========
236 258
237 mp_state_enum result_ssl = STATE_OK;
238/* certificate checks */
239#ifdef LIBCURL_FEATURE_SSL 259#ifdef LIBCURL_FEATURE_SSL
240 if (workingState.use_ssl) { 260 if (workingState.use_ssl && config.check_cert) {
241 if (config.check_cert) { 261 mp_subcheck sc_certificate = check_curl_certificate_checks(
242 if (is_openssl_callback) { 262 curl_state.curl, cert, config.days_till_exp_warn, config.days_till_exp_crit);
243# ifdef USE_OPENSSL
244 /* check certificate with OpenSSL functions, curl has been built against OpenSSL
245 * and we actually have OpenSSL in the monitoring tools
246 */
247 result_ssl = np_net_ssl_check_certificate(cert, config.days_till_exp_warn,
248 config.days_till_exp_crit);
249 if (!config.continue_after_check_cert) {
250 return result_ssl;
251 }
252# else /* USE_OPENSSL */
253 die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL "
254 "callback used and not linked against OpenSSL\n");
255# endif /* USE_OPENSSL */
256 } else {
257 struct curl_slist *slist;
258 263
259 cert_ptr_union cert_ptr = {0}; 264 mp_add_subcheck_to_subcheck(&sc_result, sc_certificate);
260 cert_ptr.to_info = NULL; 265 if (!config.continue_after_check_cert) {
261 res = curl_easy_getinfo(curl_state.curl, CURLINFO_CERTINFO, &cert_ptr.to_info); 266 // TODO finish here then
262 if (!res && cert_ptr.to_info) {
263# ifdef USE_OPENSSL
264 /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert
265 * parsing We only check the first certificate and assume it's the one of
266 * the server
267 */
268 char *raw_cert = NULL;
269 bool got_first_cert = false;
270 for (int i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) {
271 if (got_first_cert) {
272 break;
273 }
274
275 for (slist = cert_ptr.to_certinfo->certinfo[i]; slist;
276 slist = slist->next) {
277 if (verbose >= 2) {
278 printf("%d ** %s\n", i, slist->data);
279 }
280 if (strncmp(slist->data, "Cert:", 5) == 0) {
281 raw_cert = &slist->data[5];
282 got_first_cert = true;
283 break;
284 }
285 }
286 }
287
288 if (!raw_cert) {
289 snprintf(msg, DEFAULT_BUFFER_SIZE,
290 _("Cannot retrieve certificates from CERTINFO information - "
291 "certificate data was empty"));
292 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
293 }
294 BIO *cert_BIO = BIO_new(BIO_s_mem());
295 BIO_write(cert_BIO, raw_cert, (int)strlen(raw_cert));
296 cert = PEM_read_bio_X509(cert_BIO, NULL, NULL, NULL);
297 if (!cert) {
298 snprintf(
299 msg, DEFAULT_BUFFER_SIZE,
300 _("Cannot read certificate from CERTINFO information - BIO error"));
301 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
302 }
303 BIO_free(cert_BIO);
304 result_ssl = np_net_ssl_check_certificate(cert, config.days_till_exp_warn,
305 config.days_till_exp_crit);
306 if (!config.continue_after_check_cert) {
307 return result_ssl;
308 }
309# else /* USE_OPENSSL */
310 /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our
311 * disposal, so we use the libcurl CURLINFO data
312 */
313 result_ssl = net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn,
314 days_till_exp_crit);
315 if (!continue_after_check_cert) {
316 return result_ssl;
317 }
318# endif /* USE_OPENSSL */
319 } else {
320 snprintf(msg, DEFAULT_BUFFER_SIZE,
321 _("Cannot retrieve certificates - cURL returned %d - %s"), res,
322 curl_easy_strerror(res));
323 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
324 }
325 }
326 } 267 }
327 } 268 }
328#endif /* LIBCURL_FEATURE_SSL */ 269#endif
329 270
330 /* we got the data and we executed the request in a given time, so we can append 271 /* we got the data and we executed the request in a given time, so we can append
331 * performance data to the answer always 272 * performance data to the answer always
332 */ 273 */
274
275 // total time the query took
276 mp_perfdata pd_total_time = perfdata_init();
333 double total_time; 277 double total_time;
334 handle_curl_option_return_code( 278 handle_curl_option_return_code(
335 curl_easy_getinfo(curl_state.curl, CURLINFO_TOTAL_TIME, &total_time), 279 curl_easy_getinfo(curl_state.curl, CURLINFO_TOTAL_TIME, &total_time),
336 "CURLINFO_TOTAL_TIME"); 280 "CURLINFO_TOTAL_TIME");
337 size_t page_len = get_content_length(curl_state.header_buf, curl_state.body_buf); 281 mp_perfdata_value pd_val_total_time = mp_create_pd_value(total_time);
338 char perfstring[DEFAULT_BUFFER_SIZE]; 282 pd_total_time.value = pd_val_total_time;
283 pd_total_time = mp_pd_set_thresholds(pd_total_time, config.thlds);
284 pd_total_time.label = "time";
285 pd_total_time.uom = "s";
286
287 mp_subcheck sc_total_time = mp_subcheck_init();
288 sc_total_time = mp_set_subcheck_state(sc_total_time, mp_get_pd_status(pd_total_time));
289 xasprintf(&sc_total_time.output, "Total connection time: %fs", total_time);
290 mp_add_perfdata_to_subcheck(&sc_total_time, pd_total_time);
291
292 mp_add_subcheck_to_subcheck(&sc_result, sc_total_time);
293
339 if (config.show_extended_perfdata) { 294 if (config.show_extended_perfdata) {
295 // overall connection time
296 mp_perfdata pd_time_connect = perfdata_init();
340 double time_connect; 297 double time_connect;
341 handle_curl_option_return_code( 298 handle_curl_option_return_code(
342 curl_easy_getinfo(curl_state.curl, CURLINFO_CONNECT_TIME, &time_connect), 299 curl_easy_getinfo(curl_state.curl, CURLINFO_CONNECT_TIME, &time_connect),
343 "CURLINFO_CONNECT_TIME"); 300 "CURLINFO_CONNECT_TIME");
344 301
302 mp_perfdata_value pd_val_time_connect = mp_create_pd_value(time_connect);
303 pd_time_connect.value = pd_val_time_connect;
304 pd_time_connect.label = "time";
305 pd_time_connect.uom = "s";
306 pd_time_connect = mp_set_pd_max_value(
307 pd_time_connect, mp_create_pd_value(config.curl_config.socket_timeout));
308
309 pd_time_connect = mp_pd_set_thresholds(pd_time_connect, config.thlds);
310 mp_add_perfdata_to_subcheck(&sc_result, pd_time_connect);
311
312 // application connection time, used to compute other timings
345 double time_appconnect; 313 double time_appconnect;
346 handle_curl_option_return_code( 314 handle_curl_option_return_code(
347 curl_easy_getinfo(curl_state.curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), 315 curl_easy_getinfo(curl_state.curl, CURLINFO_APPCONNECT_TIME, &time_appconnect),
348 "CURLINFO_APPCONNECT_TIME"); 316 "CURLINFO_APPCONNECT_TIME");
349 317
350 double time_headers; 318 if (workingState.use_ssl) {
351 handle_curl_option_return_code( 319 mp_perfdata pd_time_tls = perfdata_init();
352 curl_easy_getinfo(curl_state.curl, CURLINFO_PRETRANSFER_TIME, &time_headers), 320 {
353 "CURLINFO_PRETRANSFER_TIME"); 321 mp_perfdata_value pd_val_time_tls =
322 mp_create_pd_value(time_appconnect - time_connect);
323
324 pd_time_tls.value = pd_val_time_tls;
325 }
326 pd_time_tls.label = "time_tls";
327 pd_time_tls.uom = "s";
328 mp_add_perfdata_to_subcheck(&sc_result, pd_time_tls);
329 }
330
331 mp_perfdata pd_time_headers = perfdata_init();
332 {
333 double time_headers;
334 handle_curl_option_return_code(
335 curl_easy_getinfo(curl_state.curl, CURLINFO_PRETRANSFER_TIME, &time_headers),
336 "CURLINFO_PRETRANSFER_TIME");
337
338 mp_perfdata_value pd_val_time_headers =
339 mp_create_pd_value(time_headers - time_appconnect);
340
341 pd_time_headers.value = pd_val_time_headers;
342 }
343 pd_time_headers.label = "time_headers";
344 pd_time_headers.uom = "s";
345 mp_add_perfdata_to_subcheck(&sc_result, pd_time_headers);
354 346
347 mp_perfdata pd_time_firstbyte = perfdata_init();
355 double time_firstbyte; 348 double time_firstbyte;
356 handle_curl_option_return_code( 349 handle_curl_option_return_code(
357 curl_easy_getinfo(curl_state.curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), 350 curl_easy_getinfo(curl_state.curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte),
358 "CURLINFO_STARTTRANSFER_TIME"); 351 "CURLINFO_STARTTRANSFER_TIME");
359 352
360 snprintf( 353 mp_perfdata_value pd_val_time_firstbyte = mp_create_pd_value(time_firstbyte);
361 perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s", 354 pd_time_firstbyte.value = pd_val_time_firstbyte;
362 perfd_time(total_time, config.thlds, config.curl_config.socket_timeout), 355 pd_time_firstbyte.label = "time_firstbyte";
363 perfd_size(page_len, config.min_page_len), 356 pd_time_firstbyte.uom = "s";
364 perfd_time_connect(time_connect, config.curl_config.socket_timeout), 357 mp_add_perfdata_to_subcheck(&sc_result, pd_time_firstbyte);
365 workingState.use_ssl 358
366 ? perfd_time_ssl(time_appconnect - time_connect, config.curl_config.socket_timeout) 359 mp_perfdata pd_time_transfer = perfdata_init();
367 : "", 360 pd_time_transfer.value = mp_create_pd_value(total_time - time_firstbyte);
368 perfd_time_headers(time_headers - time_appconnect, config.curl_config.socket_timeout), 361 pd_time_transfer.label = "time_transfer";
369 perfd_time_firstbyte(time_firstbyte - time_headers, config.curl_config.socket_timeout), 362 pd_time_transfer.uom = "s";
370 perfd_time_transfer(total_time - time_firstbyte, config.curl_config.socket_timeout)); 363 mp_add_perfdata_to_subcheck(&sc_result, pd_time_transfer);
371 } else {
372 snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s",
373 perfd_time(total_time, config.thlds, config.curl_config.socket_timeout),
374 perfd_size(page_len, config.min_page_len));
375 } 364 }
376 365
377 /* return a CRITICAL status if we couldn't read any data */ 366 /* return a CRITICAL status if we couldn't read any data */
378 if (strlen(curl_state.header_buf->buf) == 0 && strlen(curl_state.body_buf->buf) == 0) { 367 if (strlen(curl_state.header_buf->buf) == 0 && strlen(curl_state.body_buf->buf) == 0) {
379 die(STATE_CRITICAL, _("HTTP CRITICAL - No header received from host\n")); 368 sc_result = mp_set_subcheck_state(sc_result, STATE_CRITICAL);
369 xasprintf(&sc_result.output, "No header received from host");
370 return sc_result;
380 } 371 }
381 372
382 /* get status line of answer, check sanity of HTTP code */ 373 /* get status line of answer, check sanity of HTTP code */
383 if (curlhelp_parse_statusline(curl_state.header_buf->buf, curl_state.status_line) < 0) { 374 if (curlhelp_parse_statusline(curl_state.header_buf->buf, curl_state.status_line) < 0) {
384 snprintf(msg, DEFAULT_BUFFER_SIZE, 375 sc_result = mp_set_subcheck_state(sc_result, STATE_CRITICAL);
385 "Unparsable status line in %.3g seconds response time|%s\n", total_time,
386 perfstring);
387 /* we cannot know the major/minor version here for sure as we cannot parse the first 376 /* we cannot know the major/minor version here for sure as we cannot parse the first
388 * line */ 377 * line */
389 die(STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x unknown - %s", msg); 378 xasprintf(&sc_result.output, "HTTP/x.x unknown - Unparsable status line");
379 return sc_result;
390 } 380 }
381
391 curl_state.status_line_initialized = true; 382 curl_state.status_line_initialized = true;
392 383
393 /* get result code from cURL */ 384 /* get result code from cURL */
@@ -406,43 +397,71 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat
406 } 397 }
407 398
408 /* make sure the status line matches the response we are looking for */ 399 /* make sure the status line matches the response we are looking for */
400 mp_subcheck sc_expect = mp_subcheck_init();
401 sc_expect = mp_set_subcheck_default_state(sc_expect, STATE_OK);
409 if (!expected_statuscode(curl_state.status_line->first_line, config.server_expect.string)) { 402 if (!expected_statuscode(curl_state.status_line->first_line, config.server_expect.string)) {
410 if (workingState.serverPort == HTTP_PORT) { 403 if (workingState.serverPort == HTTP_PORT) {
411 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), 404 xasprintf(&sc_expect.output, _("Invalid HTTP response received from host: %s\n"),
412 curl_state.status_line->first_line); 405 curl_state.status_line->first_line);
413 } else { 406 } else {
414 snprintf(msg, DEFAULT_BUFFER_SIZE, 407 xasprintf(&sc_expect.output,
415 _("Invalid HTTP response received from host on port %d: %s\n"), 408 _("Invalid HTTP response received from host on port %d: %s\n"),
416 workingState.serverPort, curl_state.status_line->first_line); 409 workingState.serverPort, curl_state.status_line->first_line);
417 } 410 }
418 die(STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg, config.show_body ? "\n" : "", 411 sc_expect = mp_set_subcheck_default_state(sc_expect, STATE_CRITICAL);
419 config.show_body ? curl_state.body_buf->buf : ""); 412 } else {
413 xasprintf(&sc_expect.output, _("Status line output matched \"%s\""),
414 config.server_expect.string);
420 } 415 }
416 mp_add_subcheck_to_subcheck(&sc_result, sc_expect);
421 417
422 mp_state_enum result = STATE_OK; 418 if (!config.server_expect.is_present) {
423 if (config.server_expect.is_present) {
424 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "),
425 config.server_expect.string);
426 if (verbose) {
427 printf("%s\n", msg);
428 }
429 result = STATE_OK;
430 } else {
431 /* illegal return codes result in a critical state */ 419 /* illegal return codes result in a critical state */
420 mp_subcheck sc_return_code = mp_subcheck_init();
421 sc_return_code = mp_set_subcheck_default_state(sc_return_code, STATE_OK);
422 xasprintf(&sc_return_code.output, "HTTP return code: %d",
423 curl_state.status_line->http_code);
424
432 if (httpReturnCode >= 600 || httpReturnCode < 100) { 425 if (httpReturnCode >= 600 || httpReturnCode < 100) {
433 die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), 426 sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_CRITICAL);
434 curl_state.status_line->http_code, curl_state.status_line->msg); 427 xasprintf(&sc_return_code.output, _("Invalid Status (%d, %.40s)"),
435 /* server errors result in a critical state */ 428 curl_state.status_line->http_code, curl_state.status_line->msg);
436 } else if (httpReturnCode >= 500) { 429 mp_add_subcheck_to_subcheck(&sc_result, sc_return_code);
437 result = STATE_CRITICAL; 430 return sc_result;
431 }
432
433 // server errors result in a critical state
434 if (httpReturnCode >= 500) {
435 sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_CRITICAL);
438 /* client errors result in a warning state */ 436 /* client errors result in a warning state */
439 } else if (httpReturnCode >= 400) { 437 } else if (httpReturnCode >= 400) {
440 result = STATE_WARNING; 438 sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_WARNING);
441 /* check redirected page if specified */ 439 /* check redirected page if specified */
442 } else if (httpReturnCode >= 300) { 440 } else if (httpReturnCode >= 300) {
443 if (config.on_redirect_dependent) { 441 if (config.on_redirect_dependent) {
444 if (config.followmethod == FOLLOW_LIBCURL) { 442 if (config.followmethod == FOLLOW_LIBCURL) {
445 httpReturnCode = curl_state.status_line->http_code; 443 httpReturnCode = curl_state.status_line->http_code;
444 handle_curl_option_return_code(
445 curl_easy_getinfo(curl_state.curl, CURLINFO_REDIRECT_COUNT, &redir_depth),
446 "CURLINFO_REDIRECT_COUNT");
447
448 if (verbose >= 2) {
449 printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth);
450 }
451
452 mp_subcheck sc_redir_depth = mp_subcheck_init();
453 if (redir_depth > config.max_depth) {
454 xasprintf(&sc_redir_depth.output,
455 "maximum redirection depth %d exceeded in libcurl",
456 config.max_depth);
457 sc_redir_depth = mp_set_subcheck_state(sc_redir_depth, STATE_CRITICAL);
458 mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth);
459 return sc_result;
460 }
461 xasprintf(&sc_redir_depth.output, "redirection depth %d (of a maximum %d)",
462 redir_depth, config.max_depth);
463 mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth);
464
446 } else { 465 } else {
447 /* old check_http style redirection, if we come 466 /* old check_http style redirection, if we come
448 * back here, we are in the same status as with 467 * 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
451 redir_wrapper redir_result = 470 redir_wrapper redir_result =
452 redir(curl_state.header_buf, config, redir_depth, workingState); 471 redir(curl_state.header_buf, config, redir_depth, workingState);
453 cleanup(curl_state); 472 cleanup(curl_state);
454 check_http(config, redir_result.working_state, redir_result.redir_depth); 473 mp_subcheck sc_redir =
474 check_http(config, redir_result.working_state, redir_result.redir_depth);
475 mp_add_subcheck_to_subcheck(&sc_result, sc_redir);
476
477 return sc_result;
455 } 478 }
456 } else { 479 } else {
457 /* this is a specific code in the command line to 480 /* this is a specific code in the command line to
458 * be returned when a redirection is encountered 481 * be returned when a redirection is encountered
459 */ 482 */
483 sc_return_code =
484 mp_set_subcheck_state(sc_return_code, config.on_redirect_result_state);
460 } 485 }
461 result = max_state_alt(config.on_redirect_result_state, result);
462 /* all other codes are considered ok */
463 } else { 486 } else {
464 result = STATE_OK; 487 sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_OK);
465 }
466 }
467
468 /* libcurl redirection internally, handle error states here */
469 if (config.followmethod == FOLLOW_LIBCURL) {
470 handle_curl_option_return_code(
471 curl_easy_getinfo(curl_state.curl, CURLINFO_REDIRECT_COUNT, &redir_depth),
472 "CURLINFO_REDIRECT_COUNT");
473
474 if (verbose >= 2) {
475 printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth);
476 } 488 }
477 489
478 if (redir_depth > config.max_depth) { 490 mp_add_subcheck_to_subcheck(&sc_result, sc_return_code);
479 snprintf(msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl",
480 config.max_depth);
481 die(STATE_WARNING, "HTTP WARNING - %s", msg);
482 }
483 } 491 }
484 492
485 /* check status codes, set exit status accordingly */ 493 /* check status codes, set exit status accordingly */
486 if (curl_state.status_line->http_code != httpReturnCode) { 494 if (curl_state.status_line->http_code != httpReturnCode) {
487 die(STATE_CRITICAL, _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"), 495 mp_subcheck sc_http_return_code_sanity = mp_subcheck_init();
488 string_statuscode(curl_state.status_line->http_major, curl_state.status_line->http_minor), 496 sc_http_return_code_sanity =
489 curl_state.status_line->http_code, curl_state.status_line->msg, httpReturnCode); 497 mp_set_subcheck_state(sc_http_return_code_sanity, STATE_CRITICAL);
498 xasprintf(&sc_http_return_code_sanity.output,
499 _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"),
500 string_statuscode(curl_state.status_line->http_major,
501 curl_state.status_line->http_minor),
502 curl_state.status_line->http_code, curl_state.status_line->msg, httpReturnCode);
503
504 mp_add_subcheck_to_subcheck(&sc_result, sc_http_return_code_sanity);
505 return sc_result;
490 } 506 }
491 507
492 if (config.maximum_age >= 0) { 508 if (config.maximum_age >= 0) {
493 result = max_state_alt( 509 mp_subcheck sc_max_age = check_document_dates(curl_state.header_buf, config.maximum_age);
494 check_document_dates(curl_state.header_buf, msg, config.maximum_age), result); 510 mp_add_subcheck_to_subcheck(&sc_result, sc_max_age);
495 } 511 }
496 512
497 /* Page and Header content checks go here */ 513 /* Page and Header content checks go here */
498
499 if (strlen(config.header_expect)) { 514 if (strlen(config.header_expect)) {
500 if (!strstr(curl_state.header_buf->buf, config.header_expect)) { 515 mp_subcheck sc_header_expect = mp_subcheck_init();
516 sc_header_expect = mp_set_subcheck_default_state(sc_header_expect, STATE_OK);
517 xasprintf(&sc_header_expect.output, "Expect %s in header", config.header_expect);
501 518
519 if (!strstr(curl_state.header_buf->buf, config.header_expect)) {
502 char output_header_search[30] = ""; 520 char output_header_search[30] = "";
503 strncpy(&output_header_search[0], config.header_expect, sizeof(output_header_search)); 521 strncpy(&output_header_search[0], config.header_expect, sizeof(output_header_search));
504 522
@@ -506,22 +524,23 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat
506 bcopy("...", &output_header_search[sizeof(output_header_search) - 4], 4); 524 bcopy("...", &output_header_search[sizeof(output_header_search) - 4], 4);
507 } 525 }
508 526
509 char tmp[DEFAULT_BUFFER_SIZE]; 527 xasprintf(&sc_header_expect.output, _("header '%s' not found on '%s://%s:%d%s', "),
510 528 output_header_search, workingState.use_ssl ? "https" : "http",
511 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sheader '%s' not found on '%s://%s:%d%s', "), 529 workingState.host_name ? workingState.host_name : workingState.server_address,
512 msg, output_header_search, workingState.use_ssl ? "https" : "http", 530 workingState.serverPort, workingState.server_url);
513 workingState.host_name ? workingState.host_name : workingState.server_address,
514 workingState.serverPort, workingState.server_url);
515
516 strcpy(msg, tmp);
517 531
518 result = STATE_CRITICAL; 532 sc_header_expect = mp_set_subcheck_state(sc_header_expect, STATE_CRITICAL);
519 } 533 }
534
535 mp_add_subcheck_to_subcheck(&sc_result, sc_header_expect);
520 } 536 }
521 537
522 if (strlen(config.string_expect)) { 538 if (strlen(config.string_expect)) {
523 if (!strstr(curl_state.body_buf->buf, config.string_expect)) { 539 mp_subcheck sc_string_expect = mp_subcheck_init();
540 sc_string_expect = mp_set_subcheck_default_state(sc_string_expect, STATE_OK);
541 xasprintf(&sc_string_expect.output, "Expect string \"%s\" in body", config.string_expect);
524 542
543 if (!strstr(curl_state.body_buf->buf, config.string_expect)) {
525 char output_string_search[30] = ""; 544 char output_string_search[30] = "";
526 strncpy(&output_string_search[0], config.string_expect, sizeof(output_string_search)); 545 strncpy(&output_string_search[0], config.string_expect, sizeof(output_string_search));
527 546
@@ -529,93 +548,86 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat
529 bcopy("...", &output_string_search[sizeof(output_string_search) - 4], 4); 548 bcopy("...", &output_string_search[sizeof(output_string_search) - 4], 4);
530 } 549 }
531 550
532 char tmp[DEFAULT_BUFFER_SIZE]; 551 xasprintf(&sc_string_expect.output, _("string '%s' not found on '%s://%s:%d%s', "),
533 552 output_string_search, workingState.use_ssl ? "https" : "http",
534 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sstring '%s' not found on '%s://%s:%d%s', "), 553 workingState.host_name ? workingState.host_name : workingState.server_address,
535 msg, output_string_search, workingState.use_ssl ? "https" : "http", 554 workingState.serverPort, workingState.server_url);
536 workingState.host_name ? workingState.host_name : workingState.server_address,
537 workingState.serverPort, workingState.server_url);
538 555
539 strcpy(msg, tmp); 556 sc_string_expect = mp_set_subcheck_state(sc_string_expect, STATE_CRITICAL);
540
541 result = STATE_CRITICAL;
542 } 557 }
558
559 mp_add_subcheck_to_subcheck(&sc_result, sc_string_expect);
543 } 560 }
544 561
545 if (strlen(config.regexp)) { 562 if (strlen(config.regexp)) {
563 mp_subcheck sc_body_regex = mp_subcheck_init();
564 xasprintf(&sc_body_regex.output, "Regex \"%s\" in body matched", config.regexp);
546 regmatch_t pmatch[REGS]; 565 regmatch_t pmatch[REGS];
566
547 int errcode = regexec(&config.compiled_regex, curl_state.body_buf->buf, REGS, pmatch, 0); 567 int errcode = regexec(&config.compiled_regex, curl_state.body_buf->buf, REGS, pmatch, 0);
548 if ((errcode == 0 && !config.invert_regex) ||
549 (errcode == REG_NOMATCH && config.invert_regex)) {
550 /* OK - No-op to avoid changing the logic around it */
551 result = max_state_alt(STATE_OK, result);
552 } else if ((errcode == REG_NOMATCH && !config.invert_regex) ||
553 (errcode == 0 && config.invert_regex)) {
554 if (!config.invert_regex) {
555 char tmp[DEFAULT_BUFFER_SIZE];
556
557 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spattern not found, "), msg);
558 strcpy(msg, tmp);
559 568
569 if (errcode == 0) {
570 // got a match
571 if (config.invert_regex) {
572 sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK);
560 } else { 573 } else {
561 char tmp[DEFAULT_BUFFER_SIZE]; 574 sc_body_regex = mp_set_subcheck_state(sc_body_regex, config.state_regex);
562
563 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spattern found, "), msg);
564 strcpy(msg, tmp);
565 } 575 }
566 result = config.state_regex;
567 } else { 576 } else {
568 regerror(errcode, &config.compiled_regex, errbuf, MAX_INPUT_BUFFER); 577 xasprintf(&sc_body_regex.output, "%s not", sc_body_regex.output);
569 578 // got no match
570 char tmp[DEFAULT_BUFFER_SIZE]; 579 if (config.invert_regex) {
571 580 sc_body_regex = mp_set_subcheck_state(sc_body_regex, config.state_regex);
572 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sExecute Error: %s, "), msg, errbuf); 581 } else {
573 strcpy(msg, tmp); 582 sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK);
574 result = STATE_UNKNOWN; 583 }
575 } 584 }
576 }
577 585
578 /* make sure the page is of an appropriate size */ 586 mp_add_subcheck_to_subcheck(&sc_result, sc_body_regex);
579 if ((config.max_page_len > 0) && (page_len > config.max_page_len)) { 587 }
580 char tmp[DEFAULT_BUFFER_SIZE];
581 588
582 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %zu too large, "), msg, page_len); 589 // size a.k.a. page length
590 mp_perfdata pd_page_length = perfdata_init();
591 size_t page_len = get_content_length(curl_state.header_buf, curl_state.body_buf);
592 mp_perfdata_value pd_val_page_length = mp_create_pd_value(page_len);
593 pd_page_length.value = pd_val_page_length;
594 pd_page_length.label = "size";
595 pd_page_length.uom = "B";
596 pd_page_length.min = mp_create_pd_value(0);
597 pd_page_length.warn = config.page_length_limits;
598 pd_page_length.warn_present = true;
583 599
584 strcpy(msg, tmp); 600 /* make sure the page is of an appropriate size */
601 if (config.page_length_limits_is_set) {
602 mp_thresholds page_length_threshold = mp_thresholds_init();
603 page_length_threshold.warning = config.page_length_limits;
604 page_length_threshold.warning_is_set = true;
585 605
586 result = max_state_alt(STATE_WARNING, result); 606 pd_page_length = mp_pd_set_thresholds(pd_page_length, page_length_threshold);
587 607
588 } else if ((config.min_page_len > 0) && (page_len < config.min_page_len)) { 608 mp_subcheck sc_page_length = mp_subcheck_init();
589 char tmp[DEFAULT_BUFFER_SIZE];
590 609
591 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %zu too small, "), msg, page_len); 610 mp_add_perfdata_to_subcheck(&sc_page_length, pd_page_length);
592 strcpy(msg, tmp);
593 result = max_state_alt(STATE_WARNING, result);
594 }
595 611
596 /* -w, -c: check warning and critical level */ 612 mp_state_enum tmp_state = mp_get_pd_status(pd_page_length);
597 result = max_state_alt(get_status(total_time, config.thlds), result); 613 sc_page_length = mp_set_subcheck_state(sc_page_length, tmp_state);
598 614
599 /* Cut-off trailing characters */ 615 switch (tmp_state) {
600 if (strlen(msg) >= 2) { 616 case STATE_CRITICAL:
601 if (msg[strlen(msg) - 2] == ',') { 617 case STATE_WARNING:
602 msg[strlen(msg) - 2] = '\0'; 618 xasprintf(&sc_page_length.output, _("page size %zu violates threshold"), page_len);
603 } else { 619 break;
604 msg[strlen(msg) - 3] = '\0'; 620 case STATE_OK:
621 xasprintf(&sc_page_length.output, _("page size %zu is OK"), page_len);
622 break;
623 default:
624 assert(false);
605 } 625 }
626
627 mp_add_subcheck_to_subcheck(&sc_result, sc_page_length);
606 } 628 }
607 629
608 /* TODO: separate _() msg and status code: die (result, "HTTP %s: %s\n", state_text(result), 630 return sc_result;
609 * msg); */
610 die((int)max_state_alt(result, result_ssl),
611 "HTTP %s: %s %d %s%s%s - %zu bytes in %.3f second response time %s|%s\n%s%s",
612 state_text(result),
613 string_statuscode(curl_state.status_line->http_major, curl_state.status_line->http_minor),
614 curl_state.status_line->http_code, curl_state.status_line->msg, strlen(msg) > 0 ? " - " : "",
615 msg, page_len, total_time, (config.display_html ? "</A>" : ""), perfstring,
616 (config.show_body ? curl_state.body_buf->buf : ""), (config.show_body ? "\n" : ""));
617
618 return max_state_alt(result, result_ssl);
619} 631}
620 632
621int uri_strcmp(const UriTextRangeA range, const char *stringToCompare) { 633int uri_strcmp(const UriTextRangeA range, const char *stringToCompare) {
@@ -895,8 +907,6 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) {
895 } 907 }
896 908
897 int option = 0; 909 int option = 0;
898 char *warning_thresholds = NULL;
899 char *critical_thresholds = NULL;
900 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE; 910 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
901 bool specify_port = false; 911 bool specify_port = false;
902 bool enable_tls = false; 912 bool enable_tls = false;
@@ -931,11 +941,22 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) {
931 } 941 }
932 break; 942 break;
933 case 'c': /* critical time threshold */ 943 case 'c': /* critical time threshold */
934 critical_thresholds = optarg; 944 {
935 break; 945 mp_range_parsed critical_range = mp_parse_range_string(optarg);
946 if (critical_range.error != MP_PARSING_SUCCES) {
947 die(STATE_UNKNOWN, "failed to parse critical threshold: %s", optarg);
948 }
949 result.config.thlds = mp_thresholds_set_crit(result.config.thlds, critical_range.range);
950 } break;
936 case 'w': /* warning time threshold */ 951 case 'w': /* warning time threshold */
937 warning_thresholds = optarg; 952 {
938 break; 953 mp_range_parsed warning_range = mp_parse_range_string(optarg);
954
955 if (warning_range.error != MP_PARSING_SUCCES) {
956 die(STATE_UNKNOWN, "failed to parse warning threshold: %s", optarg);
957 }
958 result.config.thlds = mp_thresholds_set_warn(result.config.thlds, warning_range.range);
959 } break;
939 case 'H': /* virtual host */ 960 case 'H': /* virtual host */
940 result.config.initial_config.host_name = strdup(optarg); 961 result.config.initial_config.host_name = strdup(optarg);
941 char *tmp_string; 962 char *tmp_string;
@@ -1207,27 +1228,14 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) {
1207 break; 1228 break;
1208 case 'm': /* min_page_length */ 1229 case 'm': /* min_page_length */
1209 { 1230 {
1210 char *tmp; 1231 mp_range_parsed foo = mp_parse_range_string(optarg);
1211 if (strchr(optarg, ':') != (char *)NULL) {
1212 /* range, so get two values, min:max */
1213 tmp = strtok(optarg, ":");
1214 if (tmp == NULL) {
1215 printf("Bad format: try \"-m min:max\"\n");
1216 exit(STATE_WARNING);
1217 } else {
1218 result.config.min_page_len = atol(tmp);
1219 }
1220 1232
1221 tmp = strtok(NULL, ":"); 1233 if (foo.error != MP_PARSING_SUCCES) {
1222 if (tmp == NULL) { 1234 die(STATE_CRITICAL, "failed to parse page size limits: %s", optarg);
1223 printf("Bad format: try \"-m min:max\"\n");
1224 exit(STATE_WARNING);
1225 } else {
1226 result.config.max_page_len = atol(tmp);
1227 }
1228 } else {
1229 result.config.min_page_len = atol(optarg);
1230 } 1235 }
1236
1237 result.config.page_length_limits = foo.range;
1238 result.config.page_length_limits_is_set = true;
1231 break; 1239 break;
1232 } 1240 }
1233 case 'N': /* no-body */ 1241 case 'N': /* no-body */
@@ -1401,16 +1409,6 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) {
1401 } 1409 }
1402 } 1410 }
1403 1411
1404 set_thresholds(&result.config.thlds, warning_thresholds, critical_thresholds);
1405
1406 if (critical_thresholds &&
1407 result.config.thlds->critical->end > (double)result.config.curl_config.socket_timeout) {
1408 result.config.curl_config.socket_timeout = (int)result.config.thlds->critical->end + 1;
1409 }
1410 if (verbose >= 2) {
1411 printf("* Socket timeout set to %ld seconds\n", result.config.curl_config.socket_timeout);
1412 }
1413
1414 if (result.config.initial_config.http_method == NULL) { 1412 if (result.config.initial_config.http_method == NULL) {
1415 result.config.initial_config.http_method = strdup("GET"); 1413 result.config.initial_config.http_method = strdup("GET");
1416 } 1414 }
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 @@
5#include <netdb.h> 5#include <netdb.h>
6#include <stdlib.h> 6#include <stdlib.h>
7#include "../utils.h" 7#include "../utils.h"
8#include "check_curl.d/config.h"
9#include "output.h"
10#include "perfdata.h"
11#include "states.h"
8 12
9extern int verbose; 13extern int verbose;
10char msg[DEFAULT_BUFFER_SIZE];
11char errbuf[MAX_INPUT_BUFFER]; 14char errbuf[MAX_INPUT_BUFFER];
12bool is_openssl_callback = false; 15bool is_openssl_callback = false;
13bool add_sslctx_verify_fun = false; 16bool add_sslctx_verify_fun = false;
@@ -127,10 +130,9 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
127 int res; 130 int res;
128 if ((res = lookup_host(working_state.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2, 131 if ((res = lookup_host(working_state.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2,
129 config.sin_family)) != 0) { 132 config.sin_family)) != 0) {
130 snprintf(msg, DEFAULT_BUFFER_SIZE, 133 die(STATE_CRITICAL,
131 _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), 134 _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"),
132 working_state.server_address, res, gai_strerror(res)); 135 working_state.server_address, res, gai_strerror(res));
133 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
134 } 136 }
135 snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", working_state.host_name, 137 snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", working_state.host_name,
136 working_state.serverPort, addrstr); 138 working_state.serverPort, addrstr);
@@ -154,12 +156,7 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
154 } 156 }
155 157
156 /* compose URL: use the address we want to connect to, set Host: header later */ 158 /* compose URL: use the address we want to connect to, set Host: header later */
157 char url[DEFAULT_BUFFER_SIZE]; 159 char *url = fmt_url(working_state);
158 snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", working_state.use_ssl ? "https" : "http",
159 (working_state.use_ssl & (working_state.host_name != NULL))
160 ? working_state.host_name
161 : working_state.server_address,
162 working_state.serverPort, working_state.server_url);
163 160
164 if (verbose >= 1) { 161 if (verbose >= 1) {
165 printf("* curl CURLOPT_URL: %s\n", url); 162 printf("* curl CURLOPT_URL: %s\n", url);
@@ -167,6 +164,8 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
167 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, url), 164 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, url),
168 "CURLOPT_URL"); 165 "CURLOPT_URL");
169 166
167 free(url);
168
170 /* extract proxy information for legacy proxy https requests */ 169 /* extract proxy information for legacy proxy https requests */
171 if (!strcmp(working_state.http_method, "CONNECT") || 170 if (!strcmp(working_state.http_method, "CONNECT") ||
172 strstr(working_state.server_url, "http") == working_state.server_url) { 171 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,
548 547
549void handle_curl_option_return_code(CURLcode res, const char *option) { 548void handle_curl_option_return_code(CURLcode res, const char *option) {
550 if (res != CURLE_OK) { 549 if (res != CURLE_OK) {
551 snprintf(msg, DEFAULT_BUFFER_SIZE, 550 die(STATE_CRITICAL, _("Error while setting cURL option '%s': cURL returned %d - %s"),
552 _("Error while setting cURL option '%s': cURL returned %d - %s"), option, res, 551 option, res, curl_easy_strerror(res));
553 curl_easy_strerror(res));
554 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
555 } 552 }
556} 553}
557 554
@@ -618,9 +615,9 @@ check_curl_config check_curl_config_init() {
618 .continue_after_check_cert = false, 615 .continue_after_check_cert = false,
619 .days_till_exp_warn = 0, 616 .days_till_exp_warn = 0,
620 .days_till_exp_crit = 0, 617 .days_till_exp_crit = 0,
621 .thlds = NULL, 618 .thlds = mp_thresholds_init(),
622 .min_page_len = 0, 619 .page_length_limits = mp_range_init(),
623 .max_page_len = 0, 620 .page_length_limits_is_set = false,
624 .server_expect = 621 .server_expect =
625 { 622 {
626 .string = HTTP_EXPECT, 623 .string = HTTP_EXPECT,
@@ -729,9 +726,7 @@ size_t get_content_length(const curlhelp_write_curlbuf *header_buf,
729 return header_buf->buflen + body_buf->buflen; 726 return header_buf->buflen + body_buf->buflen;
730} 727}
731 728
732mp_state_enum check_document_dates(const curlhelp_write_curlbuf *header_buf, 729mp_subcheck check_document_dates(const curlhelp_write_curlbuf *header_buf, const int maximum_age) {
733 const char msg[static DEFAULT_BUFFER_SIZE],
734 const int maximum_age) {
735 struct phr_header headers[255]; 730 struct phr_header headers[255];
736 size_t nof_headers = 255; 731 size_t nof_headers = 255;
737 curlhelp_statusline status_line; 732 curlhelp_statusline status_line;
@@ -747,73 +742,54 @@ mp_state_enum check_document_dates(const curlhelp_write_curlbuf *header_buf,
747 char *server_date = get_header_value(headers, nof_headers, "date"); 742 char *server_date = get_header_value(headers, nof_headers, "date");
748 char *document_date = get_header_value(headers, nof_headers, "last-modified"); 743 char *document_date = get_header_value(headers, nof_headers, "last-modified");
749 744
750 mp_state_enum date_result = STATE_OK; 745 mp_subcheck sc_document_dates = mp_subcheck_init();
751 if (!server_date || !*server_date) { 746 if (!server_date || !*server_date) {
752 char tmp[DEFAULT_BUFFER_SIZE]; 747 xasprintf(&sc_document_dates.output, _("Server date unknown"));
753 748 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_UNKNOWN);
754 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date unknown, "), msg);
755 strcpy(msg, tmp);
756
757 date_result = max_state_alt(STATE_UNKNOWN, date_result);
758
759 } else if (!document_date || !*document_date) { 749 } else if (!document_date || !*document_date) {
760 char tmp[DEFAULT_BUFFER_SIZE]; 750 xasprintf(&sc_document_dates.output, _("Document modification date unknown, "));
761 751 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
762 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument modification date unknown, "), msg);
763 strcpy(msg, tmp);
764
765 date_result = max_state_alt(STATE_CRITICAL, date_result);
766
767 } else { 752 } else {
768 time_t srv_data = curl_getdate(server_date, NULL); 753 time_t srv_data = curl_getdate(server_date, NULL);
769 time_t doc_data = curl_getdate(document_date, NULL); 754 time_t doc_data = curl_getdate(document_date, NULL);
755
770 if (verbose >= 2) { 756 if (verbose >= 2) {
771 printf("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data, 757 printf("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data,
772 document_date, (int)doc_data); 758 document_date, (int)doc_data);
773 } 759 }
774 if (srv_data <= 0) {
775 char tmp[DEFAULT_BUFFER_SIZE];
776 760
777 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date \"%100s\" unparsable, "), msg, 761 if (srv_data <= 0) {
778 server_date); 762 xasprintf(&sc_document_dates.output, _("Server date \"%100s\" unparsable"),
779 strcpy(msg, tmp); 763 server_date);
780 764 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
781 date_result = max_state_alt(STATE_CRITICAL, date_result);
782 } else if (doc_data <= 0) { 765 } else if (doc_data <= 0) {
783 char tmp[DEFAULT_BUFFER_SIZE];
784
785 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument date \"%100s\" unparsable, "), msg,
786 document_date);
787 strcpy(msg, tmp);
788 766
789 date_result = max_state_alt(STATE_CRITICAL, date_result); 767 xasprintf(&sc_document_dates.output, _("Document date \"%100s\" unparsable"),
768 document_date);
769 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
790 } else if (doc_data > srv_data + 30) { 770 } else if (doc_data > srv_data + 30) {
791 char tmp[DEFAULT_BUFFER_SIZE];
792 771
793 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument is %d seconds in the future, "), msg, 772 xasprintf(&sc_document_dates.output, _("Document is %d seconds in the future"),
794 (int)doc_data - (int)srv_data); 773 (int)doc_data - (int)srv_data);
795 strcpy(msg, tmp);
796 774
797 date_result = max_state_alt(STATE_CRITICAL, date_result); 775 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
798 } else if (doc_data < srv_data - maximum_age) { 776 } else if (doc_data < srv_data - maximum_age) {
799 time_t last_modified = (srv_data - doc_data); 777 time_t last_modified = (srv_data - doc_data);
800 if (last_modified > (60 * 60 * 24 * 2)) { 778 if (last_modified > (60 * 60 * 24 * 2)) { // two days hardcoded?
801 char tmp[DEFAULT_BUFFER_SIZE]; 779 xasprintf(&sc_document_dates.output, _("Last modified %.1f days ago"),
802 780 ((float)last_modified) / (60 * 60 * 24));
803 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), msg, 781 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
804 ((float)last_modified) / (60 * 60 * 24));
805 strcpy(msg, tmp);
806
807 date_result = max_state_alt(STATE_CRITICAL, date_result);
808 } else { 782 } else {
809 char tmp[DEFAULT_BUFFER_SIZE]; 783 xasprintf(&sc_document_dates.output, _("Last modified %ld:%02ld:%02ld ago"),
810 784 last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60);
811 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %ld:%02ld:%02ld ago, "), msg, 785 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
812 last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60);
813 strcpy(msg, tmp);
814
815 date_result = max_state_alt(STATE_CRITICAL, date_result);
816 } 786 }
787 } else {
788 // TODO is this the OK case?
789 time_t last_modified = (srv_data - doc_data);
790 xasprintf(&sc_document_dates.output, _("Last modified %ld:%02ld:%02ld ago"),
791 last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60);
792 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_OK);
817 } 793 }
818 } 794 }
819 795
@@ -824,7 +800,7 @@ mp_state_enum check_document_dates(const curlhelp_write_curlbuf *header_buf,
824 free(document_date); 800 free(document_date);
825 } 801 }
826 802
827 return date_result; 803 return sc_document_dates;
828} 804}
829 805
830void curlhelp_free_statusline(curlhelp_statusline *status_line) { free(status_line->first_line); } 806void curlhelp_free_statusline(curlhelp_statusline *status_line) { free(status_line->first_line); }
@@ -1172,46 +1148,117 @@ char *string_statuscode(int major, int minor) {
1172 return buf; 1148 return buf;
1173} 1149}
1174 1150
1175char *perfd_time(double elapsed_time, thresholds *thlds, long socket_timeout) { 1151/* check whether a file exists */
1176 return fperfdata("time", elapsed_time, "s", (thlds->warning != NULL), 1152void test_file(char *path) {
1177 thlds->warning ? thlds->warning->end : 0, (thlds->critical != NULL), 1153 if (access(path, R_OK) == 0) {
1178 thlds->critical ? thlds->critical->end : 0, true, 0, true, socket_timeout); 1154 return;
1155 }
1156 usage2(_("file does not exist or is not readable"), path);
1179} 1157}
1180 1158
1181char *perfd_time_connect(double elapsed_time_connect, long socket_timeout) { 1159mp_subcheck np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn,
1182 return fperfdata("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, 1160 int days_till_exp_crit);
1183 socket_timeout);
1184}
1185 1161
1186char *perfd_time_ssl(double elapsed_time_ssl, long socket_timeout) { 1162mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_till_exp,
1187 return fperfdata("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, 1163 int crit_days_till_exp) {
1188 socket_timeout); 1164 mp_subcheck sc_cert_result = mp_subcheck_init();
1189} 1165 sc_cert_result = mp_set_subcheck_default_state(sc_cert_result, STATE_OK);
1190 1166
1191char *perfd_time_headers(double elapsed_time_headers, long socket_timeout) { 1167#ifdef LIBCURL_FEATURE_SSL
1192 return fperfdata("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, 1168 if (is_openssl_callback) {
1193 socket_timeout); 1169# ifdef USE_OPENSSL
1194} 1170 /* check certificate with OpenSSL functions, curl has been built against OpenSSL
1171 * and we actually have OpenSSL in the monitoring tools
1172 */
1173 return np_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp);
1174# else /* USE_OPENSSL */
1175 xasprintf(&result.output, "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL "
1176 "callback used and not linked against OpenSSL\n");
1177 mp_set_subcheck_state(result, STATE_CRITICAL);
1178# endif /* USE_OPENSSL */
1179 } else {
1180 struct curl_slist *slist;
1181
1182 cert_ptr_union cert_ptr = {0};
1183 cert_ptr.to_info = NULL;
1184 CURLcode res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &cert_ptr.to_info);
1185 if (!res && cert_ptr.to_info) {
1186# ifdef USE_OPENSSL
1187 /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert
1188 * parsing We only check the first certificate and assume it's the one of
1189 * the server
1190 */
1191 char *raw_cert = NULL;
1192 bool got_first_cert = false;
1193 for (int i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) {
1194 if (got_first_cert) {
1195 break;
1196 }
1197
1198 for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) {
1199 if (verbose >= 2) {
1200 printf("%d ** %s\n", i, slist->data);
1201 }
1202 if (strncmp(slist->data, "Cert:", 5) == 0) {
1203 raw_cert = &slist->data[5];
1204 got_first_cert = true;
1205 break;
1206 }
1207 }
1208 }
1195 1209
1196char *perfd_time_firstbyte(double elapsed_time_firstbyte, long socket_timeout) { 1210 if (!raw_cert) {
1197 return fperfdata("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0,
1198 true, socket_timeout);
1199}
1200 1211
1201char *perfd_time_transfer(double elapsed_time_transfer, long socket_timeout) { 1212 xasprintf(&sc_cert_result.output,
1202 return fperfdata("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, 1213 _("Cannot retrieve certificates from CERTINFO information - "
1203 true, socket_timeout); 1214 "certificate data was empty"));
1204} 1215 sc_cert_result = mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL);
1216 return sc_cert_result;
1217 }
1218
1219 BIO *cert_BIO = BIO_new(BIO_s_mem());
1220 BIO_write(cert_BIO, raw_cert, (int)strlen(raw_cert));
1221
1222 cert = PEM_read_bio_X509(cert_BIO, NULL, NULL, NULL);
1223 if (!cert) {
1224 xasprintf(&sc_cert_result.output,
1225 _("Cannot read certificate from CERTINFO information - BIO error"));
1226 sc_cert_result = mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL);
1227 return sc_cert_result;
1228 }
1205 1229
1206char *perfd_size(size_t page_len, int min_page_len) { 1230 BIO_free(cert_BIO);
1207 return perfdata("size", page_len, "B", (min_page_len > 0), min_page_len, (min_page_len > 0), 0, 1231 return np_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp);
1208 true, 0, false, 0); 1232# else /* USE_OPENSSL */
1233 /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our
1234 * disposal, so we use the libcurl CURLINFO data
1235 */
1236 return net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn,
1237 days_till_exp_crit);
1238# endif /* USE_OPENSSL */
1239 } else {
1240 xasprintf(&sc_cert_result.output,
1241 _("Cannot retrieve certificates - cURL returned %d - %s"), res,
1242 curl_easy_strerror(res));
1243 mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL);
1244 }
1245 }
1246#endif /* LIBCURL_FEATURE_SSL */
1247
1248 return sc_cert_result;
1209} 1249}
1210 1250
1211/* check whether a file exists */ 1251char *fmt_url(check_curl_working_state workingState) {
1212void test_file(char *path) { 1252 char *url = calloc(DEFAULT_BUFFER_SIZE, sizeof(char));
1213 if (access(path, R_OK) == 0) { 1253 if (url == NULL) {
1214 return; 1254 die(STATE_UNKNOWN, "memory allocation failed");
1215 } 1255 }
1216 usage2(_("file does not exist or is not readable"), path); 1256
1257 snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", workingState.use_ssl ? "https" : "http",
1258 (workingState.use_ssl & (workingState.host_name != NULL))
1259 ? workingState.host_name
1260 : workingState.server_address,
1261 workingState.serverPort, workingState.server_url);
1262
1263 return url;
1217} 1264}
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 @@
1#include "./config.h" 1#include "./config.h"
2#include <curl/curl.h> 2#include <curl/curl.h>
3#include "../picohttpparser/picohttpparser.h" 3#include "../picohttpparser/picohttpparser.h"
4// #include "curl/easy.h" 4#include "output.h"
5
6#if defined(HAVE_SSL) && defined(USE_OPENSSL)
7# include <openssl/opensslv.h>
8#endif
5 9
6/* for buffers for header and body */ 10/* for buffers for header and body */
7typedef struct { 11typedef struct {
@@ -99,8 +103,8 @@ int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * /*stat
99void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/); 103void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/);
100 104
101char *get_header_value(const struct phr_header *headers, size_t nof_headers, const char *header); 105char *get_header_value(const struct phr_header *headers, size_t nof_headers, const char *header);
102mp_state_enum check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, 106mp_subcheck check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/,
103 const char msg[static DEFAULT_BUFFER_SIZE], int /*maximum_age*/); 107 int /*maximum_age*/);
104size_t get_content_length(const curlhelp_write_curlbuf *header_buf, 108size_t get_content_length(const curlhelp_write_curlbuf *header_buf,
105 const curlhelp_write_curlbuf *body_buf); 109 const curlhelp_write_curlbuf *body_buf);
106int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family); 110int 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);
114bool expected_statuscode(const char *reply, const char *statuscodes); 118bool expected_statuscode(const char *reply, const char *statuscodes);
115char *string_statuscode(int major, int minor); 119char *string_statuscode(int major, int minor);
116 120
117char *perfd_time(double elapsed_time, thresholds * /*thlds*/, long /*socket_timeout*/);
118char *perfd_time_connect(double elapsed_time_connect, long /*socket_timeout*/);
119char *perfd_time_ssl(double elapsed_time_ssl, long /*socket_timeout*/);
120char *perfd_time_firstbyte(double elapsed_time_firstbyte, long /*socket_timeout*/);
121char *perfd_time_headers(double elapsed_time_headers, long /*socket_timeout*/);
122char *perfd_time_transfer(double elapsed_time_transfer, long /*socket_timeout*/);
123char *perfd_size(size_t page_len, int /*min_page_len*/);
124
125void test_file(char *path); 121void test_file(char *path);
122mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_till_exp,
123 int crit_days_till_exp);
124char *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 @@
8#include <string.h> 8#include <string.h>
9#include <sys/socket.h> 9#include <sys/socket.h>
10#include "curl/curl.h" 10#include "curl/curl.h"
11#include "perfdata.h"
11#include "regex.h" 12#include "regex.h"
12 13
13enum { 14enum {
@@ -93,9 +94,9 @@ typedef struct {
93 bool continue_after_check_cert; 94 bool continue_after_check_cert;
94 int days_till_exp_warn; 95 int days_till_exp_warn;
95 int days_till_exp_crit; 96 int days_till_exp_crit;
96 thresholds *thlds; 97 mp_thresholds thlds;
97 size_t min_page_len; 98 mp_range page_length_limits;
98 size_t max_page_len; 99 bool page_length_limits_is_set;
99 struct { 100 struct {
100 char string[MAX_INPUT_BUFFER]; 101 char string[MAX_INPUT_BUFFER];
101 bool is_present; 102 bool is_present;