summaryrefslogtreecommitdiffstats
path: root/plugins/check_curl.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/check_curl.c')
-rw-r--r--plugins/check_curl.c2886
1 files changed, 1066 insertions, 1820 deletions
diff --git a/plugins/check_curl.c b/plugins/check_curl.c
index 748201e8..fc704171 100644
--- a/plugins/check_curl.c
+++ b/plugins/check_curl.c
@@ -32,16 +32,23 @@
32 * 32 *
33 * 33 *
34 *****************************************************************************/ 34 *****************************************************************************/
35const char *progname = "check_curl";
36 35
36const char *progname = "check_curl";
37const char *copyright = "2006-2024"; 37const char *copyright = "2006-2024";
38const char *email = "devel@monitoring-plugins.org"; 38const char *email = "devel@monitoring-plugins.org";
39 39
40#include "check_curl.d/config.h"
41#include "states.h"
42#include "thresholds.h"
40#include <stdbool.h> 43#include <stdbool.h>
41#include <ctype.h> 44#include <ctype.h>
45#include "output.h"
46#include "perfdata.h"
42 47
48#include <assert.h>
43#include "common.h" 49#include "common.h"
44#include "utils.h" 50#include "utils.h"
51#include "./check_curl.d/check_curl_helpers.h"
45 52
46#ifndef LIBCURL_PROTOCOL_HTTP 53#ifndef LIBCURL_PROTOCOL_HTTP
47# error libcurl compiled without HTTP support, compiling check_curl plugin does not makes a lot of sense 54# error libcurl compiled without HTTP support, compiling check_curl plugin does not makes a lot of sense
@@ -50,8 +57,6 @@ const char *email = "devel@monitoring-plugins.org";
50#include "curl/curl.h" 57#include "curl/curl.h"
51#include "curl/easy.h" 58#include "curl/easy.h"
52 59
53#include "picohttpparser.h"
54
55#include "uriparser/Uri.h" 60#include "uriparser/Uri.h"
56 61
57#include <arpa/inet.h> 62#include <arpa/inet.h>
@@ -63,207 +68,62 @@ const char *email = "devel@monitoring-plugins.org";
63 68
64#include <netdb.h> 69#include <netdb.h>
65 70
66#define MAKE_LIBCURL_VERSION(major, minor, patch) ((major)*0x10000 + (minor)*0x100 + (patch))
67
68#define DEFAULT_BUFFER_SIZE 2048
69#define DEFAULT_SERVER_URL "/"
70#define HTTP_EXPECT "HTTP/"
71#define INET_ADDR_MAX_SIZE INET6_ADDRSTRLEN
72enum { 71enum {
73 MAX_IPV4_HOSTLENGTH = 255, 72 MAX_IPV4_HOSTLENGTH = 255,
74 HTTP_PORT = 80,
75 HTTPS_PORT = 443,
76 MAX_PORT = 65535,
77 DEFAULT_MAX_REDIRS = 15
78}; 73};
79 74
80enum { 75enum {
81 STICKY_NONE = 0, 76 REGS = 2,
82 STICKY_HOST = 1,
83 STICKY_PORT = 2
84};
85
86enum {
87 FOLLOW_HTTP_CURL = 0,
88 FOLLOW_LIBCURL = 1
89}; 77};
90 78
91/* for buffers for header and body */ 79#include "regex.h"
92typedef struct {
93 char *buf;
94 size_t buflen;
95 size_t bufsize;
96} curlhelp_write_curlbuf;
97 80
98/* for buffering the data sent in PUT */ 81// Globals
99typedef struct { 82int verbose = 0;
100 char *buf;
101 size_t buflen;
102 off_t pos;
103} curlhelp_read_curlbuf;
104 83
105/* for parsing the HTTP status line */ 84extern char errbuf[MAX_INPUT_BUFFER];
106typedef struct { 85extern bool is_openssl_callback;
107 int http_major; /* major version of the protocol, always 1 (HTTP/0.9 86extern bool add_sslctx_verify_fun;
108 * never reached the big internet most likely) */
109 int http_minor; /* minor version of the protocol, usually 0 or 1 */
110 int http_code; /* HTTP return code as in RFC 2145 */
111 int http_subcode; /* Microsoft IIS extension, HTTP subcodes, see
112 * http://support.microsoft.com/kb/318380/en-us */
113 const char *msg; /* the human readable message */
114 char *first_line; /* a copy of the first line */
115} curlhelp_statusline;
116
117/* to know the underlying SSL library used by libcurl */
118typedef enum curlhelp_ssl_library {
119 CURLHELP_SSL_LIBRARY_UNKNOWN,
120 CURLHELP_SSL_LIBRARY_OPENSSL,
121 CURLHELP_SSL_LIBRARY_LIBRESSL,
122 CURLHELP_SSL_LIBRARY_GNUTLS,
123 CURLHELP_SSL_LIBRARY_NSS
124} curlhelp_ssl_library;
125 87
126enum {
127 REGS = 2,
128 MAX_RE_SIZE = 1024
129};
130#include "regex.h"
131static regex_t preg;
132static regmatch_t pmatch[REGS];
133static char regexp[MAX_RE_SIZE];
134static int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
135static int errcode;
136static bool invert_regex = false;
137static int state_regex = STATE_CRITICAL;
138
139static char *server_address = NULL;
140static char *host_name = NULL;
141static char *server_url = 0;
142static struct curl_slist *server_ips = NULL;
143static bool specify_port = false;
144static unsigned short server_port = HTTP_PORT;
145static unsigned short virtual_port = 0;
146static int host_name_length;
147static char output_header_search[30] = "";
148static char output_string_search[30] = "";
149static char *warning_thresholds = NULL;
150static char *critical_thresholds = NULL;
151static int days_till_exp_warn, days_till_exp_crit;
152static thresholds *thlds;
153static char user_agent[DEFAULT_BUFFER_SIZE];
154static int verbose = 0;
155static bool show_extended_perfdata = false;
156static bool show_body = false;
157static int min_page_len = 0;
158static int max_page_len = 0;
159static int redir_depth = 0;
160static int max_depth = DEFAULT_MAX_REDIRS;
161static char *http_method = NULL;
162static char *http_post_data = NULL;
163static char *http_content_type = NULL;
164static CURL *curl;
165static bool curl_global_initialized = false;
166static bool curl_easy_initialized = false;
167static struct curl_slist *header_list = NULL;
168static bool body_buf_initialized = false;
169static curlhelp_write_curlbuf body_buf;
170static bool header_buf_initialized = false;
171static curlhelp_write_curlbuf header_buf;
172static bool status_line_initialized = false;
173static curlhelp_statusline status_line;
174static bool put_buf_initialized = false;
175static curlhelp_read_curlbuf put_buf;
176static char http_header[DEFAULT_BUFFER_SIZE];
177static long code;
178static long socket_timeout = DEFAULT_SOCKET_TIMEOUT;
179static double total_time;
180static double time_connect;
181static double time_appconnect;
182static double time_headers;
183static double time_firstbyte;
184static char errbuf[MAX_INPUT_BUFFER];
185static CURLcode res;
186static char url[DEFAULT_BUFFER_SIZE];
187static char msg[DEFAULT_BUFFER_SIZE];
188static char perfstring[DEFAULT_BUFFER_SIZE];
189static char header_expect[MAX_INPUT_BUFFER] = "";
190static char string_expect[MAX_INPUT_BUFFER] = "";
191static char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
192static int server_expect_yn = 0;
193static char user_auth[MAX_INPUT_BUFFER] = "";
194static char proxy_auth[MAX_INPUT_BUFFER] = "";
195static char **http_opt_headers;
196static int http_opt_headers_count = 0;
197static bool display_html = false;
198static int onredirect = STATE_OK;
199static int followmethod = FOLLOW_HTTP_CURL;
200static int followsticky = STICKY_NONE;
201static bool use_ssl = false;
202static bool check_cert = false;
203static bool continue_after_check_cert = false;
204typedef union {
205 struct curl_slist *to_info;
206 struct curl_certinfo *to_certinfo;
207} cert_ptr_union;
208static cert_ptr_union cert_ptr;
209static int ssl_version = CURL_SSLVERSION_DEFAULT;
210static char *client_cert = NULL;
211static char *client_privkey = NULL;
212static char *ca_cert = NULL;
213static bool verify_peer_and_host = false;
214static bool is_openssl_callback = false;
215static bool add_sslctx_verify_fun = false;
216#if defined(HAVE_SSL) && defined(USE_OPENSSL) 88#if defined(HAVE_SSL) && defined(USE_OPENSSL)
217static X509 *cert = NULL; 89static X509 *cert = NULL;
218#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ 90#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */
219static bool no_body = false; 91
220static int maximum_age = -1; 92typedef struct {
221static int address_family = AF_UNSPEC; 93 int errorcode;
222static curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN; 94 check_curl_config config;
223static int curl_http_version = CURL_HTTP_VERSION_NONE; 95} check_curl_config_wrapper;
224static bool automatic_decompression = false; 96static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
225static char *cookie_jar_file = NULL; 97
226static bool haproxy_protocol = false; 98static mp_subcheck check_http(check_curl_config /*config*/, check_curl_working_state workingState,
227 99 int redir_depth);
228static bool process_arguments(int /*argc*/, char ** /*argv*/); 100
229static void handle_curl_option_return_code(CURLcode res, const char *option); 101typedef struct {
230static int check_http(void); 102 int redir_depth;
231static void redir(curlhelp_write_curlbuf * /*header_buf*/); 103 check_curl_working_state working_state;
232static char *perfd_time(double elapsed_time); 104 int error_code;
233static char *perfd_time_connect(double elapsed_time_connect); 105 check_curl_global_state curl_state;
234static char *perfd_time_ssl(double elapsed_time_ssl); 106} redir_wrapper;
235static char *perfd_time_firstbyte(double elapsed_time_firstbyte); 107static redir_wrapper redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/,
236static char *perfd_time_headers(double elapsed_time_headers); 108 int redir_depth, check_curl_working_state working_state);
237static char *perfd_time_transfer(double elapsed_time_transfer); 109
238static char *perfd_size(int page_len);
239static void print_help(void); 110static void print_help(void);
240void print_usage(void); 111void print_usage(void);
112
241static void print_curl_version(void); 113static void print_curl_version(void);
242static int curlhelp_initwritebuffer(curlhelp_write_curlbuf * /*buf*/); 114
243static size_t curlhelp_buffer_write_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, void * /*stream*/); 115// typedef struct {
244static void curlhelp_freewritebuffer(curlhelp_write_curlbuf * /*buf*/); 116// int errorcode;
245static int curlhelp_initreadbuffer(curlhelp_read_curlbuf * /*buf*/, const char * /*data*/, size_t /*datalen*/); 117// } check_curl_evaluation_wrapper;
246static size_t curlhelp_buffer_read_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, void * /*stream*/); 118// check_curl_evaluation_wrapper check_curl_evaluate(check_curl_config config,
247static void curlhelp_freereadbuffer(curlhelp_read_curlbuf * /*buf*/); 119// mp_check overall[static 1]) {}
248static curlhelp_ssl_library curlhelp_get_ssl_library(void);
249static const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library /*ssl_library*/);
250int net_noopenssl_check_certificate(cert_ptr_union *, int, int);
251
252static int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * /*status_line*/);
253static void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/);
254static char *get_header_value(const struct phr_header *headers, size_t nof_headers, const char *header);
255static int check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, char (*msg)[DEFAULT_BUFFER_SIZE]);
256static int get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_write_curlbuf *body_buf);
257 120
258#if defined(HAVE_SSL) && defined(USE_OPENSSL) 121#if defined(HAVE_SSL) && defined(USE_OPENSSL)
259int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit); 122mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn,
123 int days_till_exp_crit);
260#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ 124#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */
261 125
262static void test_file(char * /*path*/);
263
264int main(int argc, char **argv) { 126int main(int argc, char **argv) {
265 int result = STATE_UNKNOWN;
266
267 setlocale(LC_ALL, ""); 127 setlocale(LC_ALL, "");
268 bindtextdomain(PACKAGE, LOCALEDIR); 128 bindtextdomain(PACKAGE, LOCALEDIR);
269 textdomain(PACKAGE); 129 textdomain(PACKAGE);
@@ -271,24 +131,30 @@ int main(int argc, char **argv) {
271 /* Parse extra opts if any */ 131 /* Parse extra opts if any */
272 argv = np_extra_opts(&argc, argv, progname); 132 argv = np_extra_opts(&argc, argv, progname);
273 133
274 /* set defaults */
275 snprintf(user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", progname, NP_VERSION, VERSION, curl_version());
276
277 /* parse arguments */ 134 /* parse arguments */
278 if (process_arguments(argc, argv) == false) 135 check_curl_config_wrapper tmp_config = process_arguments(argc, argv);
136 if (tmp_config.errorcode == ERROR) {
279 usage4(_("Could not parse arguments")); 137 usage4(_("Could not parse arguments"));
138 }
280 139
281 if (display_html) 140 const check_curl_config config = tmp_config.config;
282 printf("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">", use_ssl ? "https" : "http", host_name ? host_name : server_address,
283 virtual_port ? virtual_port : server_port, server_url);
284 141
285 result = check_http(); 142 if (config.output_format_is_set) {
286 return result; 143 mp_set_format(config.output_format);
144 }
145
146 check_curl_working_state working_state = config.initial_config;
147
148 mp_check overall = mp_check_init();
149 mp_subcheck sc_test = check_http(config, working_state, 0);
150
151 mp_add_subcheck_to_check(&overall, sc_test);
152
153 mp_exit(overall);
287} 154}
288 155
289#ifdef HAVE_SSL 156#ifdef HAVE_SSL
290# ifdef USE_OPENSSL 157# ifdef USE_OPENSSL
291
292int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { 158int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) {
293 (void)preverify_ok; 159 (void)preverify_ok;
294 /* TODO: we get all certificates of the chain, so which ones 160 /* TODO: we get all certificates of the chain, so which ones
@@ -301,19 +167,21 @@ int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) {
301# endif 167# endif
302 if (verbose >= 2) { 168 if (verbose >= 2) {
303 puts("* SSL verify callback with certificate:"); 169 puts("* SSL verify callback with certificate:");
304 X509_NAME *subject;
305 X509_NAME *issuer;
306 printf("* issuer:\n"); 170 printf("* issuer:\n");
307 issuer = X509_get_issuer_name(cert); 171 X509_NAME *issuer = X509_get_issuer_name(cert);
308 X509_NAME_print_ex_fp(stdout, issuer, 5, XN_FLAG_MULTILINE); 172 X509_NAME_print_ex_fp(stdout, issuer, 5, XN_FLAG_MULTILINE);
309 printf("* curl verify_callback:\n* subject:\n"); 173 printf("* curl verify_callback:\n* subject:\n");
310 subject = X509_get_subject_name(cert); 174 X509_NAME *subject = X509_get_subject_name(cert);
311 X509_NAME_print_ex_fp(stdout, subject, 5, XN_FLAG_MULTILINE); 175 X509_NAME_print_ex_fp(stdout, subject, 5, XN_FLAG_MULTILINE);
312 puts(""); 176 puts("");
313 } 177 }
314 return 1; 178 return 1;
315} 179}
180# endif /* USE_OPENSSL */
181#endif /* HAVE_SSL */
316 182
183#ifdef HAVE_SSL
184# ifdef USE_OPENSSL
317CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) { 185CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) {
318 (void)curl; // ignore unused parameter 186 (void)curl; // ignore unused parameter
319 (void)parm; // ignore unused parameter 187 (void)parm; // ignore unused parameter
@@ -330,877 +198,503 @@ CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) {
330 198
331 return CURLE_OK; 199 return CURLE_OK;
332} 200}
333
334# endif /* USE_OPENSSL */ 201# endif /* USE_OPENSSL */
335#endif /* HAVE_SSL */ 202#endif /* HAVE_SSL */
336 203
337/* returns a string "HTTP/1.x" or "HTTP/2" */ 204mp_subcheck check_http(const check_curl_config config, check_curl_working_state workingState,
338static char *string_statuscode(int major, int minor) { 205 int redir_depth) {
339 static char buf[10];
340
341 switch (major) {
342 case 1:
343 snprintf(buf, sizeof(buf), "HTTP/%d.%d", major, minor);
344 break;
345 case 2:
346 case 3:
347 snprintf(buf, sizeof(buf), "HTTP/%d", major);
348 break;
349 default:
350 /* assuming here HTTP/N with N>=4 */
351 snprintf(buf, sizeof(buf), "HTTP/%d", major);
352 break;
353 }
354 206
355 return buf; 207 // =======================
356} 208 // Initialisation for curl
209 // =======================
210 check_curl_configure_curl_wrapper conf_curl_struct = check_curl_configure_curl(
211 config.curl_config, workingState, config.check_cert, config.on_redirect_dependent,
212 config.followmethod, config.max_depth);
357 213
358/* Checks if the server 'reply' is one of the expected 'statuscodes' */ 214 check_curl_global_state curl_state = conf_curl_struct.curl_state;
359static int expected_statuscode(const char *reply, const char *statuscodes) { 215 workingState = conf_curl_struct.working_state;
360 char *expected;
361 char *code;
362 int result = 0;
363 216
364 if ((expected = strdup(statuscodes)) == NULL) 217 mp_subcheck sc_result = mp_subcheck_init();
365 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
366 218
367 for (code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) 219 char *url = fmt_url(workingState);
368 if (strstr(reply, code) != NULL) { 220 xasprintf(&sc_result.output, "Testing %s", url);
369 result = 1; 221 // TODO add some output here URL or something
370 break; 222 free(url);
371 }
372
373 free(expected);
374 return result;
375}
376 223
377void handle_curl_option_return_code(CURLcode res, const char *option) { 224 // ==============
378 if (res != CURLE_OK) { 225 // do the request
379 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Error while setting cURL option '%s': cURL returned %d - %s"), option, res, 226 // ==============
380 curl_easy_strerror(res)); 227 CURLcode res = curl_easy_perform(curl_state.curl);
381 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
382 }
383}
384
385int lookup_host(const char *host, char *buf, size_t buflen) {
386 struct addrinfo hints, *res, *result;
387 char addrstr[100];
388 size_t addrstr_len;
389 int errcode;
390 void *ptr = {0};
391 size_t buflen_remaining = buflen - 1;
392
393 memset(&hints, 0, sizeof(hints));
394 hints.ai_family = address_family;
395 hints.ai_socktype = SOCK_STREAM;
396 hints.ai_flags |= AI_CANONNAME;
397
398 errcode = getaddrinfo(host, NULL, &hints, &result);
399 if (errcode != 0)
400 return errcode;
401
402 strcpy(buf, "");
403 res = result;
404
405 while (res) {
406 switch (res->ai_family) {
407 case AF_INET:
408 ptr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
409 break;
410 case AF_INET6:
411 ptr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
412 break;
413 }
414
415 inet_ntop(res->ai_family, ptr, addrstr, 100);
416 if (verbose >= 1) {
417 printf("* getaddrinfo IPv%d address: %s\n", res->ai_family == PF_INET6 ? 6 : 4, addrstr);
418 }
419
420 // Append all IPs to buf as a comma-separated string
421 addrstr_len = strlen(addrstr);
422 if (buflen_remaining > addrstr_len + 1) {
423 if (buf[0] != '\0') {
424 strncat(buf, ",", buflen_remaining);
425 buflen_remaining -= 1;
426 }
427 strncat(buf, addrstr, buflen_remaining);
428 buflen_remaining -= addrstr_len;
429 }
430
431 res = res->ai_next;
432 }
433
434 freeaddrinfo(result);
435
436 return 0;
437}
438
439static void cleanup(void) {
440 if (status_line_initialized)
441 curlhelp_free_statusline(&status_line);
442 status_line_initialized = false;
443 if (curl_easy_initialized)
444 curl_easy_cleanup(curl);
445 curl_easy_initialized = false;
446 if (curl_global_initialized)
447 curl_global_cleanup();
448 curl_global_initialized = false;
449 if (body_buf_initialized)
450 curlhelp_freewritebuffer(&body_buf);
451 body_buf_initialized = false;
452 if (header_buf_initialized)
453 curlhelp_freewritebuffer(&header_buf);
454 header_buf_initialized = false;
455 if (put_buf_initialized)
456 curlhelp_freereadbuffer(&put_buf);
457 put_buf_initialized = false;
458}
459 228
460int check_http(void) { 229 if (verbose >= 2 && workingState.http_post_data) {
461 int result = STATE_OK; 230 printf("**** REQUEST CONTENT ****\n%s\n", workingState.http_post_data);
462 int result_ssl = STATE_OK;
463 int page_len = 0;
464 int i;
465 char *force_host_header = NULL;
466 struct curl_slist *host = NULL;
467 char addrstr[DEFAULT_BUFFER_SIZE / 2];
468 char dnscache[DEFAULT_BUFFER_SIZE];
469
470 /* initialize curl */
471 if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK)
472 die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n");
473 curl_global_initialized = true;
474
475 if ((curl = curl_easy_init()) == NULL) {
476 die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n");
477 } 231 }
478 curl_easy_initialized = true;
479 232
480 /* register cleanup function to shut down libcurl properly */ 233 mp_subcheck sc_curl = mp_subcheck_init();
481 atexit(cleanup);
482 234
483 if (verbose >= 1) 235 /* Curl errors, result in critical Nagios state */
484 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_VERBOSE, 1), "CURLOPT_VERBOSE"); 236 if (res != CURLE_OK) {
485 237 xasprintf(&sc_curl.output, _("Error while performing connection: cURL returned %d - %s"),
486 /* print everything on stdout like check_http would do */ 238 res, errbuf[0] ? errbuf : curl_easy_strerror(res));
487 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_STDERR, stdout), "CURLOPT_STDERR"); 239 sc_curl = mp_set_subcheck_state(sc_curl, STATE_CRITICAL);
488 240 mp_add_subcheck_to_subcheck(&sc_result, sc_curl);
489 if (automatic_decompression) 241 return sc_result;
490#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6)
491 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""), "CURLOPT_ACCEPT_ENCODING");
492#else
493 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING");
494#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */
495
496 /* initialize buffer for body of the answer */
497 if (curlhelp_initwritebuffer(&body_buf) < 0)
498 die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n");
499 body_buf_initialized = true;
500 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback),
501 "CURLOPT_WRITEFUNCTION");
502 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&body_buf), "CURLOPT_WRITEDATA");
503
504 /* initialize buffer for header of the answer */
505 if (curlhelp_initwritebuffer(&header_buf) < 0)
506 die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n");
507 header_buf_initialized = true;
508 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback),
509 "CURLOPT_HEADERFUNCTION");
510 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&header_buf), "CURLOPT_WRITEHEADER");
511
512 /* set the error buffer */
513 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf), "CURLOPT_ERRORBUFFER");
514
515 /* set timeouts */
516 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, socket_timeout), "CURLOPT_CONNECTTIMEOUT");
517 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_TIMEOUT, socket_timeout), "CURLOPT_TIMEOUT");
518
519 /* enable haproxy protocol */
520 if (haproxy_protocol) {
521 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HAPROXYPROTOCOL, 1L), "CURLOPT_HAPROXYPROTOCOL");
522 } 242 }
523 243
524 // fill dns resolve cache to make curl connect to the given server_address instead of the host_name, only required for ssl, because we 244 /* get status line of answer, check sanity of HTTP code */
525 // use the host_name later on to make SNI happy 245 if (curlhelp_parse_statusline(curl_state.header_buf->buf, curl_state.status_line) < 0) {
526 if (use_ssl && host_name != NULL) { 246 sc_result = mp_set_subcheck_state(sc_result, STATE_CRITICAL);
527 if ((res = lookup_host(server_address, addrstr, DEFAULT_BUFFER_SIZE / 2)) != 0) { 247 /* we cannot know the major/minor version here for sure as we cannot parse the first
528 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), server_address, res, 248 * line */
529 gai_strerror(res)); 249 xasprintf(&sc_result.output, "HTTP/x.x unknown - Unparsable status line");
530 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); 250 return sc_result;
531 }
532 snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", host_name, server_port, addrstr);
533 host = curl_slist_append(NULL, dnscache);
534 curl_easy_setopt(curl, CURLOPT_RESOLVE, host);
535 if (verbose >= 1)
536 printf("* curl CURLOPT_RESOLVE: %s\n", dnscache);
537 } 251 }
538 252
539 // If server_address is an IPv6 address it must be surround by square brackets 253 curl_state.status_line_initialized = true;
540 struct in6_addr tmp_in_addr;
541 if (inet_pton(AF_INET6, server_address, &tmp_in_addr) == 1) {
542 char *new_server_address = malloc(strlen(server_address) + 3);
543 if (new_server_address == NULL) {
544 die(STATE_UNKNOWN, "HTTP UNKNOWN - Unable to allocate memory\n");
545 }
546 snprintf(new_server_address, strlen(server_address) + 3, "[%s]", server_address);
547 free(server_address);
548 server_address = new_server_address;
549 }
550 254
551 /* compose URL: use the address we want to connect to, set Host: header later */ 255 size_t page_len = get_content_length(curl_state.header_buf, curl_state.body_buf);
552 snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", use_ssl ? "https" : "http",
553 (use_ssl & (host_name != NULL)) ? host_name : server_address, server_port, server_url);
554
555 if (verbose >= 1)
556 printf("* curl CURLOPT_URL: %s\n", url);
557 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, url), "CURLOPT_URL");
558
559 /* extract proxy information for legacy proxy https requests */
560 if (!strcmp(http_method, "CONNECT") || strstr(server_url, "http") == server_url) {
561 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXY, server_address), "CURLOPT_PROXY");
562 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYPORT, (long)server_port), "CURLOPT_PROXYPORT");
563 if (verbose >= 2)
564 printf("* curl CURLOPT_PROXY: %s:%d\n", server_address, server_port);
565 http_method = "GET";
566 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, server_url), "CURLOPT_URL");
567 }
568 256
569 /* disable body for HEAD request */ 257 double total_time;
570 if (http_method && !strcmp(http_method, "HEAD")) { 258 handle_curl_option_return_code(
571 no_body = true; 259 curl_easy_getinfo(curl_state.curl, CURLINFO_TOTAL_TIME, &total_time),
572 } 260 "CURLINFO_TOTAL_TIME");
573 261
574 /* set HTTP protocol version */ 262 xasprintf(
575 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, curl_http_version), "CURLOPT_HTTP_VERSION"); 263 &sc_curl.output, "%s %d %s - %ld bytes in %.3f second response time",
576 264 string_statuscode(curl_state.status_line->http_major, curl_state.status_line->http_minor),
577 /* set HTTP method */ 265 curl_state.status_line->http_code, curl_state.status_line->msg, page_len, total_time);
578 if (http_method) { 266 sc_curl = mp_set_subcheck_state(sc_curl, STATE_OK);
579 if (!strcmp(http_method, "POST")) 267 mp_add_subcheck_to_subcheck(&sc_result, sc_curl);
580 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POST, 1), "CURLOPT_POST");
581 else if (!strcmp(http_method, "PUT"))
582 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD");
583 else
584 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, http_method), "CURLOPT_CUSTOMREQUEST");
585 }
586 268
587 /* check if Host header is explicitly set in options */ 269 // ==========
588 if (http_opt_headers_count) { 270 // Evaluation
589 for (i = 0; i < http_opt_headers_count; i++) { 271 // ==========
590 if (strncmp(http_opt_headers[i], "Host:", 5) == 0) {
591 force_host_header = http_opt_headers[i];
592 }
593 }
594 }
595
596 /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in anyway */
597 if (host_name != NULL && force_host_header == NULL) {
598 if ((virtual_port != HTTP_PORT && !use_ssl) || (virtual_port != HTTPS_PORT && use_ssl)) {
599 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", host_name, virtual_port);
600 } else {
601 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", host_name);
602 }
603 header_list = curl_slist_append(header_list, http_header);
604 }
605
606 /* always close connection, be nice to servers */
607 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Connection: close");
608 header_list = curl_slist_append(header_list, http_header);
609
610 /* attach additional headers supplied by the user */
611 /* optionally send any other header tag */
612 if (http_opt_headers_count) {
613 for (i = 0; i < http_opt_headers_count; i++) {
614 header_list = curl_slist_append(header_list, http_opt_headers[i]);
615 }
616 /* This cannot be free'd here because a redirection will then try to access this and segfault */
617 /* Covered in a testcase in tests/check_http.t */
618 /* free(http_opt_headers); */
619 }
620
621 /* set HTTP headers */
622 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list), "CURLOPT_HTTPHEADER");
623 272
624#ifdef LIBCURL_FEATURE_SSL 273#ifdef LIBCURL_FEATURE_SSL
274 if (workingState.use_ssl && config.check_cert) {
275 mp_subcheck sc_certificate = check_curl_certificate_checks(
276 curl_state.curl, cert, config.days_till_exp_warn, config.days_till_exp_crit);
625 277
626 /* set SSL version, warn about insecure or unsupported versions */ 278 mp_add_subcheck_to_subcheck(&sc_result, sc_certificate);
627 if (use_ssl) { 279 if (!config.continue_after_check_cert) {
628 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLVERSION, ssl_version), "CURLOPT_SSLVERSION"); 280 return sc_result;
629 }
630
631 /* client certificate and key to present to server (SSL) */
632 if (client_cert)
633 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLCERT, client_cert), "CURLOPT_SSLCERT");
634 if (client_privkey)
635 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLKEY, client_privkey), "CURLOPT_SSLKEY");
636 if (ca_cert) {
637 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CAINFO, ca_cert), "CURLOPT_CAINFO");
638 }
639 if (ca_cert || verify_peer_and_host) {
640 /* per default if we have a CA verify both the peer and the
641 * hostname in the certificate, can be switched off later */
642 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1), "CURLOPT_SSL_VERIFYPEER");
643 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2), "CURLOPT_SSL_VERIFYHOST");
644 } else {
645 /* backward-compatible behaviour, be tolerant in checks
646 * TODO: depending on more options have aspects we want
647 * to be less tolerant about ssl verfications
648 */
649 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0), "CURLOPT_SSL_VERIFYPEER");
650 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0), "CURLOPT_SSL_VERIFYHOST");
651 }
652
653 /* detect SSL library used by libcurl */
654 ssl_library = curlhelp_get_ssl_library();
655
656 /* try hard to get a stack of certificates to verify against */
657 if (check_cert) {
658# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1)
659 /* inform curl to report back certificates */
660 switch (ssl_library) {
661 case CURLHELP_SSL_LIBRARY_OPENSSL:
662 case CURLHELP_SSL_LIBRARY_LIBRESSL:
663 /* set callback to extract certificate with OpenSSL context function (works with
664 * OpenSSL-style libraries only!) */
665# ifdef USE_OPENSSL
666 /* libcurl and monitoring plugins built with OpenSSL, good */
667 add_sslctx_verify_fun = true;
668 is_openssl_callback = true;
669# endif /* USE_OPENSSL */
670 /* libcurl is built with OpenSSL, monitoring plugins, so falling
671 * back to manually extracting certificate information */
672 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
673 break;
674
675 case CURLHELP_SSL_LIBRARY_NSS:
676# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
677 /* NSS: support for CERTINFO is implemented since 7.34.0 */
678 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
679# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
680 die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library '%s' is too old)\n",
681 curlhelp_get_ssl_library_string(ssl_library));
682# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
683 break;
684
685 case CURLHELP_SSL_LIBRARY_GNUTLS:
686# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0)
687 /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */
688 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
689# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
690 die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library '%s' is too old)\n",
691 curlhelp_get_ssl_library_string(ssl_library));
692# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
693 break;
694
695 case CURLHELP_SSL_LIBRARY_UNKNOWN:
696 default:
697 die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (unknown SSL library '%s', must implement first)\n",
698 curlhelp_get_ssl_library_string(ssl_library));
699 break;
700 } 281 }
701# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
702 /* old libcurl, our only hope is OpenSSL, otherwise we are out of luck */
703 if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL || ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL)
704 add_sslctx_verify_fun = true;
705 else
706 die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl "
707 "too old and has no CURLOPT_CERTINFO)\n");
708# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
709 } 282 }
710
711# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 10, 6) /* required for CURLOPT_SSL_CTX_FUNCTION */
712 // ssl ctx function is not available with all ssl backends
713 if (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, NULL) != CURLE_UNKNOWN_OPTION)
714 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION");
715# endif
716
717#endif /* LIBCURL_FEATURE_SSL */
718
719 /* set default or user-given user agent identification */
720 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent), "CURLOPT_USERAGENT");
721
722 /* proxy-authentication */
723 if (strcmp(proxy_auth, ""))
724 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy_auth), "CURLOPT_PROXYUSERPWD");
725
726 /* authentication */
727 if (strcmp(user_auth, ""))
728 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERPWD, user_auth), "CURLOPT_USERPWD");
729
730 /* TODO: parameter auth method, bitfield of following methods:
731 * CURLAUTH_BASIC (default)
732 * CURLAUTH_DIGEST
733 * CURLAUTH_DIGEST_IE
734 * CURLAUTH_NEGOTIATE
735 * CURLAUTH_NTLM
736 * CURLAUTH_NTLM_WB
737 *
738 * convenience tokens for typical sets of methods:
739 * CURLAUTH_ANYSAFE: most secure, without BASIC
740 * or CURLAUTH_ANY: most secure, even BASIC if necessary
741 *
742 * handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_DIGEST ), "CURLOPT_HTTPAUTH");
743 */
744
745 /* handle redirections */
746 if (onredirect == STATE_DEPENDENT) {
747 if (followmethod == FOLLOW_LIBCURL) {
748 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1), "CURLOPT_FOLLOWLOCATION");
749
750 /* default -1 is infinite, not good, could lead to zombie plugins!
751 Setting it to one bigger than maximal limit to handle errors nicely below
752 */
753 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_MAXREDIRS, max_depth + 1), "CURLOPT_MAXREDIRS");
754
755 /* for now allow only http and https (we are a http(s) check plugin in the end) */
756#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 85, 0)
757 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"),
758 "CURLOPT_REDIR_PROTOCOLS_STR");
759#elif LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4)
760 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS),
761 "CURLOPT_REDIRECT_PROTOCOLS");
762#endif 283#endif
763 284
764 /* TODO: handle the following aspects of redirection, make them 285 /* we got the data and we executed the request in a given time, so we can append
765 * command line options too later: 286 * performance data to the answer always
766 CURLOPT_POSTREDIR: method switch 287 */
767 CURLINFO_REDIRECT_URL: custom redirect option
768 CURLOPT_REDIRECT_PROTOCOLS: allow people to step outside safe protocols
769 CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range option here is nice like for expected page size?
770 */
771 } else {
772 /* old style redirection is handled below */
773 }
774 }
775
776 /* no-body */
777 if (no_body)
778 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_NOBODY, 1), "CURLOPT_NOBODY");
779
780 /* IPv4 or IPv6 forced DNS resolution */
781 if (address_family == AF_UNSPEC)
782 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER),
783 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)");
784 else if (address_family == AF_INET)
785 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4),
786 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)");
787#if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6)
788 else if (address_family == AF_INET6)
789 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6),
790 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)");
791#endif
792 288
793 /* either send http POST data (any data, not only POST)*/ 289 // total time the query took
794 if (!strcmp(http_method, "POST") || !strcmp(http_method, "PUT")) { 290 mp_perfdata pd_total_time = perfdata_init();
795 /* set content of payload for POST and PUT */ 291 mp_perfdata_value pd_val_total_time = mp_create_pd_value(total_time);
796 if (http_content_type) { 292 pd_total_time.value = pd_val_total_time;
797 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", http_content_type); 293 pd_total_time = mp_pd_set_thresholds(pd_total_time, config.thlds);
798 header_list = curl_slist_append(header_list, http_header); 294 pd_total_time.label = "time";
799 } 295 pd_total_time.uom = "s";
800 /* NULL indicates "HTTP Continue" in libcurl, provide an empty string 296
801 * in case of no POST/PUT data */ 297 mp_subcheck sc_total_time = mp_subcheck_init();
802 if (!http_post_data) 298 sc_total_time = mp_set_subcheck_state(sc_total_time, mp_get_pd_status(pd_total_time));
803 http_post_data = ""; 299 xasprintf(&sc_total_time.output, "Total connection time: %fs", total_time);
804 if (!strcmp(http_method, "POST")) { 300 mp_add_perfdata_to_subcheck(&sc_total_time, pd_total_time);
805 /* POST method, set payload with CURLOPT_POSTFIELDS */ 301
806 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POSTFIELDS, http_post_data), "CURLOPT_POSTFIELDS"); 302 mp_add_subcheck_to_subcheck(&sc_result, sc_total_time);
807 } else if (!strcmp(http_method, "PUT")) { 303
808 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_READFUNCTION, (curl_read_callback)curlhelp_buffer_read_callback), 304 if (config.show_extended_perfdata) {
809 "CURLOPT_READFUNCTION"); 305 // overall connection time
810 if (curlhelp_initreadbuffer(&put_buf, http_post_data, strlen(http_post_data)) < 0) 306 mp_perfdata pd_time_connect = perfdata_init();
811 die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating read buffer for PUT\n"); 307 double time_connect;
812 put_buf_initialized = true; 308 handle_curl_option_return_code(
813 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_READDATA, (void *)&put_buf), "CURLOPT_READDATA"); 309 curl_easy_getinfo(curl_state.curl, CURLINFO_CONNECT_TIME, &time_connect),
814 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_INFILESIZE, (curl_off_t)strlen(http_post_data)), 310 "CURLINFO_CONNECT_TIME");
815 "CURLOPT_INFILESIZE"); 311
312 mp_perfdata_value pd_val_time_connect = mp_create_pd_value(time_connect);
313 pd_time_connect.value = pd_val_time_connect;
314 pd_time_connect.label = "time_connect";
315 pd_time_connect.uom = "s";
316 pd_time_connect = mp_set_pd_max_value(
317 pd_time_connect, mp_create_pd_value(config.curl_config.socket_timeout));
318
319 pd_time_connect = mp_pd_set_thresholds(pd_time_connect, config.thlds);
320 mp_add_perfdata_to_subcheck(&sc_result, pd_time_connect);
321
322 // application connection time, used to compute other timings
323 double time_appconnect;
324 handle_curl_option_return_code(
325 curl_easy_getinfo(curl_state.curl, CURLINFO_APPCONNECT_TIME, &time_appconnect),
326 "CURLINFO_APPCONNECT_TIME");
327
328 if (workingState.use_ssl) {
329 mp_perfdata pd_time_tls = perfdata_init();
330 {
331 mp_perfdata_value pd_val_time_tls =
332 mp_create_pd_value(time_appconnect - time_connect);
333
334 pd_time_tls.value = pd_val_time_tls;
335 }
336 pd_time_tls.label = "time_tls";
337 pd_time_tls.uom = "s";
338 mp_add_perfdata_to_subcheck(&sc_result, pd_time_tls);
816 } 339 }
817 }
818
819 /* cookie handling */
820 if (cookie_jar_file != NULL) {
821 /* enable reading cookies from a file, and if the filename is an empty string, only enable the curl cookie engine */
822 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookie_jar_file), "CURLOPT_COOKIEFILE");
823 /* now enable saving cookies to a file, but only if the filename is not an empty string, since writing it would fail */
824 if (*cookie_jar_file)
825 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookie_jar_file), "CURLOPT_COOKIEJAR");
826 }
827
828 /* do the request */
829 res = curl_easy_perform(curl);
830 340
831 if (verbose >= 2 && http_post_data) 341 mp_perfdata pd_time_headers = perfdata_init();
832 printf("**** REQUEST CONTENT ****\n%s\n", http_post_data); 342 {
343 double time_headers;
344 handle_curl_option_return_code(
345 curl_easy_getinfo(curl_state.curl, CURLINFO_PRETRANSFER_TIME, &time_headers),
346 "CURLINFO_PRETRANSFER_TIME");
833 347
834 /* free header and server IP resolve lists, we don't need it anymore */ 348 mp_perfdata_value pd_val_time_headers =
835 curl_slist_free_all(header_list); 349 mp_create_pd_value(time_headers - time_appconnect);
836 header_list = NULL;
837 curl_slist_free_all(server_ips);
838 server_ips = NULL;
839 if (host) {
840 curl_slist_free_all(host);
841 host = NULL;
842 }
843 350
844 /* Curl errors, result in critical Nagios state */ 351 pd_time_headers.value = pd_val_time_headers;
845 if (res != CURLE_OK) { 352 }
846 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"), server_port, 353 pd_time_headers.label = "time_headers";
847 res, errbuf[0] ? errbuf : curl_easy_strerror(res)); 354 pd_time_headers.uom = "s";
848 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); 355 mp_add_perfdata_to_subcheck(&sc_result, pd_time_headers);
849 }
850 356
851 /* certificate checks */ 357 mp_perfdata pd_time_firstbyte = perfdata_init();
852#ifdef LIBCURL_FEATURE_SSL 358 double time_firstbyte;
853 if (use_ssl) { 359 handle_curl_option_return_code(
854 if (check_cert) { 360 curl_easy_getinfo(curl_state.curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte),
855 if (is_openssl_callback) { 361 "CURLINFO_STARTTRANSFER_TIME");
856# ifdef USE_OPENSSL
857 /* check certificate with OpenSSL functions, curl has been built against OpenSSL
858 * and we actually have OpenSSL in the monitoring tools
859 */
860 result_ssl = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit);
861 if (!continue_after_check_cert) {
862 return result_ssl;
863 }
864# else /* USE_OPENSSL */
865 die(STATE_CRITICAL,
866 "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL callback used and not linked against OpenSSL\n");
867# endif /* USE_OPENSSL */
868 } else {
869 int i;
870 struct curl_slist *slist;
871 362
872 cert_ptr.to_info = NULL; 363 mp_perfdata_value pd_val_time_firstbyte = mp_create_pd_value(time_firstbyte);
873 res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &cert_ptr.to_info); 364 pd_time_firstbyte.value = pd_val_time_firstbyte;
874 if (!res && cert_ptr.to_info) { 365 pd_time_firstbyte.label = "time_firstbyte";
875# ifdef USE_OPENSSL 366 pd_time_firstbyte.uom = "s";
876 /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert parsing 367 mp_add_perfdata_to_subcheck(&sc_result, pd_time_firstbyte);
877 * We only check the first certificate and assume it's the one of the server
878 */
879 const char *raw_cert = NULL;
880 for (i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) {
881 for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) {
882 if (verbose >= 2)
883 printf("%d ** %s\n", i, slist->data);
884 if (strncmp(slist->data, "Cert:", 5) == 0) {
885 raw_cert = &slist->data[5];
886 goto GOT_FIRST_CERT;
887 }
888 }
889 }
890 GOT_FIRST_CERT:
891 if (!raw_cert) {
892 snprintf(msg, DEFAULT_BUFFER_SIZE,
893 _("Cannot retrieve certificates from CERTINFO information - certificate data was empty"));
894 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
895 }
896 BIO *cert_BIO = BIO_new(BIO_s_mem());
897 BIO_write(cert_BIO, raw_cert, strlen(raw_cert));
898 cert = PEM_read_bio_X509(cert_BIO, NULL, NULL, NULL);
899 if (!cert) {
900 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Cannot read certificate from CERTINFO information - BIO error"));
901 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
902 }
903 BIO_free(cert_BIO);
904 result_ssl = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit);
905 if (!continue_after_check_cert) {
906 return result_ssl;
907 }
908# else /* USE_OPENSSL */
909 /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our disposal,
910 * so we use the libcurl CURLINFO data
911 */
912 result_ssl = net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn, days_till_exp_crit);
913 if (!continue_after_check_cert) {
914 return result_ssl;
915 }
916# endif /* USE_OPENSSL */
917 } else {
918 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Cannot retrieve certificates - cURL returned %d - %s"), res,
919 curl_easy_strerror(res));
920 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
921 }
922 }
923 }
924 }
925#endif /* LIBCURL_FEATURE_SSL */
926 368
927 /* we got the data and we executed the request in a given time, so we can append 369 mp_perfdata pd_time_transfer = perfdata_init();
928 * performance data to the answer always 370 pd_time_transfer.value = mp_create_pd_value(total_time - time_firstbyte);
929 */ 371 pd_time_transfer.label = "time_transfer";
930 handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total_time), "CURLINFO_TOTAL_TIME"); 372 pd_time_transfer.uom = "s";
931 page_len = get_content_length(&header_buf, &body_buf); 373 mp_add_perfdata_to_subcheck(&sc_result, pd_time_transfer);
932 if (show_extended_perfdata) {
933 handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &time_connect), "CURLINFO_CONNECT_TIME");
934 handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), "CURLINFO_APPCONNECT_TIME");
935 handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, &time_headers), "CURLINFO_PRETRANSFER_TIME");
936 handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte),
937 "CURLINFO_STARTTRANSFER_TIME");
938 snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s", perfd_time(total_time), perfd_size(page_len),
939 perfd_time_connect(time_connect), use_ssl ? perfd_time_ssl(time_appconnect - time_connect) : "",
940 perfd_time_headers(time_headers - time_appconnect), perfd_time_firstbyte(time_firstbyte - time_headers),
941 perfd_time_transfer(total_time - time_firstbyte));
942 } else {
943 snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s", perfd_time(total_time), perfd_size(page_len));
944 } 374 }
945 375
946 /* return a CRITICAL status if we couldn't read any data */ 376 /* return a CRITICAL status if we couldn't read any data */
947 if (strlen(header_buf.buf) == 0 && strlen(body_buf.buf) == 0) 377 if (strlen(curl_state.header_buf->buf) == 0 && strlen(curl_state.body_buf->buf) == 0) {
948 die(STATE_CRITICAL, _("HTTP CRITICAL - No header received from host\n")); 378 sc_result = mp_set_subcheck_state(sc_result, STATE_CRITICAL);
949 379 xasprintf(&sc_result.output, "No header received from host");
950 /* get status line of answer, check sanity of HTTP code */ 380 return sc_result;
951 if (curlhelp_parse_statusline(header_buf.buf, &status_line) < 0) {
952 snprintf(msg, DEFAULT_BUFFER_SIZE, "Unparsable status line in %.3g seconds response time|%s\n", total_time, perfstring);
953 /* we cannot know the major/minor version here for sure as we cannot parse the first line */
954 die(STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x %ld unknown - %s", code, msg);
955 } 381 }
956 status_line_initialized = true;
957 382
958 /* get result code from cURL */ 383 /* get result code from cURL */
959 handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code), "CURLINFO_RESPONSE_CODE"); 384 long httpReturnCode;
960 if (verbose >= 2) 385 handle_curl_option_return_code(
961 printf("* curl CURLINFO_RESPONSE_CODE is %ld\n", code); 386 curl_easy_getinfo(curl_state.curl, CURLINFO_RESPONSE_CODE, &httpReturnCode),
387 "CURLINFO_RESPONSE_CODE");
388 if (verbose >= 2) {
389 printf("* curl CURLINFO_RESPONSE_CODE is %ld\n", httpReturnCode);
390 }
962 391
963 /* print status line, header, body if verbose */ 392 /* print status line, header, body if verbose */
964 if (verbose >= 2) { 393 if (verbose >= 2) {
965 printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header_buf.buf, (no_body ? " [[ skipped ]]" : body_buf.buf)); 394 printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", curl_state.header_buf->buf,
395 (workingState.no_body ? " [[ skipped ]]" : curl_state.body_buf->buf));
966 } 396 }
967 397
968 /* make sure the status line matches the response we are looking for */ 398 /* make sure the status line matches the response we are looking for */
969 if (!expected_statuscode(status_line.first_line, server_expect)) { 399 mp_subcheck sc_expect = mp_subcheck_init();
970 if (server_port == HTTP_PORT) 400 sc_expect = mp_set_subcheck_default_state(sc_expect, STATE_OK);
971 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), status_line.first_line); 401 if (!expected_statuscode(curl_state.status_line->first_line, config.server_expect.string)) {
972 else 402 if (workingState.serverPort == HTTP_PORT) {
973 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), server_port, 403 xasprintf(&sc_expect.output, _("Invalid HTTP response received from host: %s\n"),
974 status_line.first_line); 404 curl_state.status_line->first_line);
975 die(STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg, show_body ? "\n" : "", show_body ? body_buf.buf : ""); 405 } else {
406 xasprintf(&sc_expect.output,
407 _("Invalid HTTP response received from host on port %d: %s\n"),
408 workingState.serverPort, curl_state.status_line->first_line);
409 }
410 sc_expect = mp_set_subcheck_default_state(sc_expect, STATE_CRITICAL);
411 } else {
412 xasprintf(&sc_expect.output, _("Status line output matched \"%s\""),
413 config.server_expect.string);
976 } 414 }
415 mp_add_subcheck_to_subcheck(&sc_result, sc_expect);
977 416
978 if (server_expect_yn) { 417 if (!config.server_expect.is_present) {
979 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), server_expect);
980 if (verbose)
981 printf("%s\n", msg);
982 result = STATE_OK;
983 } else {
984 /* illegal return codes result in a critical state */ 418 /* illegal return codes result in a critical state */
985 if (code >= 600 || code < 100) { 419 mp_subcheck sc_return_code = mp_subcheck_init();
986 die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), status_line.http_code, status_line.msg); 420 sc_return_code = mp_set_subcheck_default_state(sc_return_code, STATE_OK);
987 /* server errors result in a critical state */ 421 xasprintf(&sc_return_code.output, "HTTP return code: %d",
988 } else if (code >= 500) { 422 curl_state.status_line->http_code);
989 result = STATE_CRITICAL; 423
424 if (httpReturnCode >= 600 || httpReturnCode < 100) {
425 sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_CRITICAL);
426 xasprintf(&sc_return_code.output, _("Invalid Status (%d, %.40s)"),
427 curl_state.status_line->http_code, curl_state.status_line->msg);
428 mp_add_subcheck_to_subcheck(&sc_result, sc_return_code);
429 return sc_result;
430 }
431
432 // server errors result in a critical state
433 if (httpReturnCode >= 500) {
434 sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_CRITICAL);
990 /* client errors result in a warning state */ 435 /* client errors result in a warning state */
991 } else if (code >= 400) { 436 } else if (httpReturnCode >= 400) {
992 result = STATE_WARNING; 437 sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_WARNING);
993 /* check redirected page if specified */ 438 /* check redirected page if specified */
994 } else if (code >= 300) { 439 } else if (httpReturnCode >= 300) {
995 if (onredirect == STATE_DEPENDENT) { 440 if (config.on_redirect_dependent) {
996 if (followmethod == FOLLOW_LIBCURL) { 441 if (config.followmethod == FOLLOW_LIBCURL) {
997 code = status_line.http_code; 442 httpReturnCode = curl_state.status_line->http_code;
443 handle_curl_option_return_code(
444 curl_easy_getinfo(curl_state.curl, CURLINFO_REDIRECT_COUNT, &redir_depth),
445 "CURLINFO_REDIRECT_COUNT");
446
447 if (verbose >= 2) {
448 printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth);
449 }
450
451 mp_subcheck sc_redir_depth = mp_subcheck_init();
452 if (redir_depth > config.max_depth) {
453 xasprintf(&sc_redir_depth.output,
454 "maximum redirection depth %d exceeded in libcurl",
455 config.max_depth);
456 sc_redir_depth = mp_set_subcheck_state(sc_redir_depth, STATE_CRITICAL);
457 mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth);
458 return sc_result;
459 }
460 xasprintf(&sc_redir_depth.output, "redirection depth %d (of a maximum %d)",
461 redir_depth, config.max_depth);
462 mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth);
463
998 } else { 464 } else {
999 /* old check_http style redirection, if we come 465 /* old check_http style redirection, if we come
1000 * back here, we are in the same status as with 466 * back here, we are in the same status as with
1001 * the libcurl method 467 * the libcurl method
1002 */ 468 */
1003 redir(&header_buf); 469 redir_wrapper redir_result =
470 redir(curl_state.header_buf, config, redir_depth, workingState);
471 cleanup(curl_state);
472 mp_subcheck sc_redir =
473 check_http(config, redir_result.working_state, redir_result.redir_depth);
474 mp_add_subcheck_to_subcheck(&sc_result, sc_redir);
475
476 return sc_result;
1004 } 477 }
1005 } else { 478 } else {
1006 /* this is a specific code in the command line to 479 /* this is a specific code in the command line to
1007 * be returned when a redirection is encountered 480 * be returned when a redirection is encountered
1008 */ 481 */
482 sc_return_code =
483 mp_set_subcheck_state(sc_return_code, config.on_redirect_result_state);
1009 } 484 }
1010 result = max_state_alt(onredirect, result);
1011 /* all other codes are considered ok */
1012 } else { 485 } else {
1013 result = STATE_OK; 486 sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_OK);
1014 } 487 }
1015 }
1016 488
1017 /* libcurl redirection internally, handle error states here */ 489 mp_add_subcheck_to_subcheck(&sc_result, sc_return_code);
1018 if (followmethod == FOLLOW_LIBCURL) {
1019 handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &redir_depth), "CURLINFO_REDIRECT_COUNT");
1020 if (verbose >= 2)
1021 printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth);
1022 if (redir_depth > max_depth) {
1023 snprintf(msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl", max_depth);
1024 die(STATE_WARNING, "HTTP WARNING - %s", msg);
1025 }
1026 } 490 }
1027 491
1028 /* check status codes, set exit status accordingly */ 492 /* check status codes, set exit status accordingly */
1029 if (status_line.http_code != code) { 493 if (curl_state.status_line->http_code != httpReturnCode) {
1030 die(STATE_CRITICAL, _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"), 494 mp_subcheck sc_http_return_code_sanity = mp_subcheck_init();
1031 string_statuscode(status_line.http_major, status_line.http_minor), status_line.http_code, status_line.msg, code); 495 sc_http_return_code_sanity =
496 mp_set_subcheck_state(sc_http_return_code_sanity, STATE_CRITICAL);
497 xasprintf(&sc_http_return_code_sanity.output,
498 _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"),
499 string_statuscode(curl_state.status_line->http_major,
500 curl_state.status_line->http_minor),
501 curl_state.status_line->http_code, curl_state.status_line->msg, httpReturnCode);
502
503 mp_add_subcheck_to_subcheck(&sc_result, sc_http_return_code_sanity);
504 return sc_result;
1032 } 505 }
1033 506
1034 if (maximum_age >= 0) { 507 if (config.maximum_age >= 0) {
1035 result = max_state_alt(check_document_dates(&header_buf, &msg), result); 508 mp_subcheck sc_max_age = check_document_dates(curl_state.header_buf, config.maximum_age);
509 mp_add_subcheck_to_subcheck(&sc_result, sc_max_age);
1036 } 510 }
1037 511
1038 /* Page and Header content checks go here */ 512 /* Page and Header content checks go here */
513 if (strlen(config.header_expect)) {
514 mp_subcheck sc_header_expect = mp_subcheck_init();
515 sc_header_expect = mp_set_subcheck_default_state(sc_header_expect, STATE_OK);
516 xasprintf(&sc_header_expect.output, "Expect %s in header", config.header_expect);
1039 517
1040 if (strlen(header_expect)) { 518 if (!strstr(curl_state.header_buf->buf, config.header_expect)) {
1041 if (!strstr(header_buf.buf, header_expect)) { 519 char output_header_search[30] = "";
1042 520 strncpy(&output_header_search[0], config.header_expect, sizeof(output_header_search));
1043 strncpy(&output_header_search[0], header_expect, sizeof(output_header_search));
1044 521
1045 if (output_header_search[sizeof(output_header_search) - 1] != '\0') { 522 if (output_header_search[sizeof(output_header_search) - 1] != '\0') {
1046 bcopy("...", &output_header_search[sizeof(output_header_search) - 4], 4); 523 bcopy("...", &output_header_search[sizeof(output_header_search) - 4], 4);
1047 } 524 }
1048 525
1049 char tmp[DEFAULT_BUFFER_SIZE]; 526 xasprintf(&sc_header_expect.output, _("header '%s' not found on '%s://%s:%d%s', "),
1050 527 output_header_search, workingState.use_ssl ? "https" : "http",
1051 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sheader '%s' not found on '%s://%s:%d%s', "), msg, output_header_search, 528 workingState.host_name ? workingState.host_name : workingState.server_address,
1052 use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url); 529 workingState.serverPort, workingState.server_url);
1053 530
1054 strcpy(msg, tmp); 531 sc_header_expect = mp_set_subcheck_state(sc_header_expect, STATE_CRITICAL);
1055
1056 result = STATE_CRITICAL;
1057 } 532 }
533
534 mp_add_subcheck_to_subcheck(&sc_result, sc_header_expect);
1058 } 535 }
1059 536
1060 if (strlen(string_expect)) { 537 if (strlen(config.string_expect)) {
1061 if (!strstr(body_buf.buf, string_expect)) { 538 mp_subcheck sc_string_expect = mp_subcheck_init();
539 sc_string_expect = mp_set_subcheck_default_state(sc_string_expect, STATE_OK);
540 xasprintf(&sc_string_expect.output, "Expect string \"%s\" in body", config.string_expect);
1062 541
1063 strncpy(&output_string_search[0], string_expect, sizeof(output_string_search)); 542 if (!strstr(curl_state.body_buf->buf, config.string_expect)) {
543 char output_string_search[30] = "";
544 strncpy(&output_string_search[0], config.string_expect, sizeof(output_string_search));
1064 545
1065 if (output_string_search[sizeof(output_string_search) - 1] != '\0') { 546 if (output_string_search[sizeof(output_string_search) - 1] != '\0') {
1066 bcopy("...", &output_string_search[sizeof(output_string_search) - 4], 4); 547 bcopy("...", &output_string_search[sizeof(output_string_search) - 4], 4);
1067 } 548 }
1068 549
1069 char tmp[DEFAULT_BUFFER_SIZE]; 550 xasprintf(&sc_string_expect.output, _("string '%s' not found on '%s://%s:%d%s', "),
1070 551 output_string_search, workingState.use_ssl ? "https" : "http",
1071 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sstring '%s' not found on '%s://%s:%d%s', "), msg, output_string_search, 552 workingState.host_name ? workingState.host_name : workingState.server_address,
1072 use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url); 553 workingState.serverPort, workingState.server_url);
1073
1074 strcpy(msg, tmp);
1075 554
1076 result = STATE_CRITICAL; 555 sc_string_expect = mp_set_subcheck_state(sc_string_expect, STATE_CRITICAL);
1077 } 556 }
557
558 mp_add_subcheck_to_subcheck(&sc_result, sc_string_expect);
1078 } 559 }
1079 560
1080 if (strlen(regexp)) { 561 if (strlen(config.regexp)) {
1081 errcode = regexec(&preg, body_buf.buf, REGS, pmatch, 0); 562 mp_subcheck sc_body_regex = mp_subcheck_init();
1082 if ((errcode == 0 && !invert_regex) || (errcode == REG_NOMATCH && invert_regex)) { 563 xasprintf(&sc_body_regex.output, "Regex \"%s\" in body matched", config.regexp);
1083 /* OK - No-op to avoid changing the logic around it */ 564 regmatch_t pmatch[REGS];
1084 result = max_state_alt(STATE_OK, result);
1085 } else if ((errcode == REG_NOMATCH && !invert_regex) || (errcode == 0 && invert_regex)) {
1086 if (!invert_regex) {
1087 char tmp[DEFAULT_BUFFER_SIZE];
1088 565
1089 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spattern not found, "), msg); 566 int errcode = regexec(&config.compiled_regex, curl_state.body_buf->buf, REGS, pmatch, 0);
1090 strcpy(msg, tmp);
1091 567
568 if (errcode == 0) {
569 // got a match
570 if (config.invert_regex) {
571 sc_body_regex = mp_set_subcheck_state(sc_body_regex, config.state_regex);
1092 } else { 572 } else {
1093 char tmp[DEFAULT_BUFFER_SIZE]; 573 sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK);
574 }
575 } else if (errcode == REG_NOMATCH) {
576 // got no match
577 xasprintf(&sc_body_regex.output, "%s not", sc_body_regex.output);
1094 578
1095 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spattern found, "), msg); 579 if (config.invert_regex) {
1096 strcpy(msg, tmp); 580 sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK);
581 } else {
582 sc_body_regex = mp_set_subcheck_state(sc_body_regex, config.state_regex);
1097 } 583 }
1098 result = state_regex;
1099 } else { 584 } else {
1100 regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); 585 // error in regexec
1101 586 char error_buffer[DEFAULT_BUFFER_SIZE];
1102 char tmp[DEFAULT_BUFFER_SIZE]; 587 regerror(errcode, &config.compiled_regex, &error_buffer[0], DEFAULT_BUFFER_SIZE);
1103 588 xasprintf(&sc_body_regex.output, "regexec error: %s", error_buffer);
1104 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sExecute Error: %s, "), msg, errbuf); 589 sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_UNKNOWN);
1105 strcpy(msg, tmp);
1106 result = STATE_UNKNOWN;
1107 } 590 }
591
592 mp_add_subcheck_to_subcheck(&sc_result, sc_body_regex);
1108 } 593 }
1109 594
1110 /* make sure the page is of an appropriate size */ 595 // size a.k.a. page length
1111 if ((max_page_len > 0) && (page_len > max_page_len)) { 596 mp_perfdata pd_page_length = perfdata_init();
1112 char tmp[DEFAULT_BUFFER_SIZE]; 597 mp_perfdata_value pd_val_page_length = mp_create_pd_value(page_len);
598 pd_page_length.value = pd_val_page_length;
599 pd_page_length.label = "size";
600 pd_page_length.uom = "B";
601 pd_page_length.min = mp_create_pd_value(0);
602 pd_page_length.warn = config.page_length_limits;
603 pd_page_length.warn_present = true;
1113 604
1114 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %d too large, "), msg, page_len); 605 /* make sure the page is of an appropriate size */
606 if (config.page_length_limits_is_set) {
607 mp_thresholds page_length_threshold = mp_thresholds_init();
608 page_length_threshold.warning = config.page_length_limits;
609 page_length_threshold.warning_is_set = true;
1115 610
1116 strcpy(msg, tmp); 611 pd_page_length = mp_pd_set_thresholds(pd_page_length, page_length_threshold);
1117 612
1118 result = max_state_alt(STATE_WARNING, result); 613 mp_subcheck sc_page_length = mp_subcheck_init();
1119 614
1120 } else if ((min_page_len > 0) && (page_len < min_page_len)) { 615 mp_add_perfdata_to_subcheck(&sc_page_length, pd_page_length);
1121 char tmp[DEFAULT_BUFFER_SIZE];
1122 616
1123 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %d too small, "), msg, page_len); 617 mp_state_enum tmp_state = mp_get_pd_status(pd_page_length);
1124 strcpy(msg, tmp); 618 sc_page_length = mp_set_subcheck_state(sc_page_length, tmp_state);
1125 result = max_state_alt(STATE_WARNING, result);
1126 }
1127 619
1128 /* -w, -c: check warning and critical level */ 620 switch (tmp_state) {
1129 result = max_state_alt(get_status(total_time, thlds), result); 621 case STATE_CRITICAL:
622 case STATE_WARNING:
623 xasprintf(&sc_page_length.output, _("page size %zu violates threshold"), page_len);
624 break;
625 case STATE_OK:
626 xasprintf(&sc_page_length.output, _("page size %zu is OK"), page_len);
627 break;
628 default:
629 assert(false);
630 }
1130 631
1131 /* Cut-off trailing characters */ 632 mp_add_subcheck_to_subcheck(&sc_result, sc_page_length);
1132 if (strlen(msg) >= 2) {
1133 if (msg[strlen(msg) - 2] == ',')
1134 msg[strlen(msg) - 2] = '\0';
1135 else
1136 msg[strlen(msg) - 3] = '\0';
1137 } 633 }
1138 634
1139 /* TODO: separate _() msg and status code: die (result, "HTTP %s: %s\n", state_text(result), msg); */ 635 return sc_result;
1140 die(max_state_alt(result, result_ssl), "HTTP %s: %s %d %s%s%s - %d bytes in %.3f second response time %s|%s\n%s%s", state_text(result),
1141 string_statuscode(status_line.http_major, status_line.http_minor), status_line.http_code, status_line.msg,
1142 strlen(msg) > 0 ? " - " : "", msg, page_len, total_time, (display_html ? "</A>" : ""), perfstring, (show_body ? body_buf.buf : ""),
1143 (show_body ? "\n" : ""));
1144
1145 return max_state_alt(result, result_ssl);
1146} 636}
1147 637
1148int uri_strcmp(const UriTextRangeA range, const char *s) { 638int uri_strcmp(const UriTextRangeA range, const char *stringToCompare) {
1149 if (!range.first) 639 if (!range.first) {
1150 return -1; 640 return -1;
1151 if ((size_t)(range.afterLast - range.first) < strlen(s)) 641 }
642 if ((size_t)(range.afterLast - range.first) < strlen(stringToCompare)) {
1152 return -1; 643 return -1;
1153 return strncmp(s, range.first, min((size_t)(range.afterLast - range.first), strlen(s))); 644 }
645 return strncmp(stringToCompare, range.first,
646 min((size_t)(range.afterLast - range.first), strlen(stringToCompare)));
1154} 647}
1155 648
1156char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) { 649char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) {
1157 if (!range.first) 650 if (!range.first) {
1158 return "(null)"; 651 return "(null)";
652 }
1159 strncpy(buf, range.first, max(buflen - 1, (size_t)(range.afterLast - range.first))); 653 strncpy(buf, range.first, max(buflen - 1, (size_t)(range.afterLast - range.first)));
1160 buf[max(buflen - 1, (size_t)(range.afterLast - range.first))] = '\0'; 654 buf[max(buflen - 1, (size_t)(range.afterLast - range.first))] = '\0';
1161 buf[range.afterLast - range.first] = '\0'; 655 buf[range.afterLast - range.first] = '\0';
1162 return buf; 656 return buf;
1163} 657}
1164 658
1165void redir(curlhelp_write_curlbuf *header_buf) { 659redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config config,
1166 char *location = NULL; 660 int redir_depth, check_curl_working_state working_state) {
1167 curlhelp_statusline status_line; 661 curlhelp_statusline status_line;
1168 struct phr_header headers[255]; 662 struct phr_header headers[255];
1169 size_t nof_headers = 255;
1170 size_t msglen; 663 size_t msglen;
1171 char buf[DEFAULT_BUFFER_SIZE]; 664 size_t nof_headers = 255;
1172 char ipstr[INET_ADDR_MAX_SIZE]; 665 int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major,
1173 int new_port; 666 &status_line.http_minor, &status_line.http_code, &status_line.msg,
1174 char *new_host; 667 &msglen, headers, &nof_headers, 0);
1175 char *new_url;
1176
1177 int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor,
1178 &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0);
1179 668
1180 if (res == -1) { 669 if (res == -1) {
1181 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); 670 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n"));
1182 } 671 }
1183 672
1184 location = get_header_value(headers, nof_headers, "location"); 673 char *location = get_header_value(headers, nof_headers, "location");
1185 674
1186 if (verbose >= 2) 675 if (verbose >= 2) {
1187 printf(_("* Seen redirect location %s\n"), location); 676 printf(_("* Seen redirect location %s\n"), location);
677 }
1188 678
1189 if (++redir_depth > max_depth) 679 if (++redir_depth > config.max_depth) {
1190 die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s%s\n"), max_depth, location, 680 die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s\n"),
1191 (display_html ? "</A>" : "")); 681 config.max_depth, location);
682 }
1192 683
1193 UriParserStateA state; 684 UriParserStateA state;
1194 UriUriA uri; 685 UriUriA uri;
1195 state.uri = &uri; 686 state.uri = &uri;
1196 if (uriParseUriA(&state, location) != URI_SUCCESS) { 687 if (uriParseUriA(&state, location) != URI_SUCCESS) {
1197 if (state.errorCode == URI_ERROR_SYNTAX) { 688 if (state.errorCode == URI_ERROR_SYNTAX) {
1198 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location '%s'%s\n"), location, (display_html ? "</A>" : "")); 689 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location '%s'\n"),
690 location);
1199 } else if (state.errorCode == URI_ERROR_MALLOC) { 691 } else if (state.errorCode == URI_ERROR_MALLOC) {
1200 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); 692 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1201 } 693 }
1202 } 694 }
1203 695
696 char ipstr[INET_ADDR_MAX_SIZE];
697 char buf[DEFAULT_BUFFER_SIZE];
1204 if (verbose >= 2) { 698 if (verbose >= 2) {
1205 printf(_("** scheme: %s\n"), uri_string(uri.scheme, buf, DEFAULT_BUFFER_SIZE)); 699 printf(_("** scheme: %s\n"), uri_string(uri.scheme, buf, DEFAULT_BUFFER_SIZE));
1206 printf(_("** host: %s\n"), uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE)); 700 printf(_("** host: %s\n"), uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE));
@@ -1215,9 +709,9 @@ void redir(curlhelp_write_curlbuf *header_buf) {
1215 } 709 }
1216 if (uri.pathHead) { 710 if (uri.pathHead) {
1217 printf(_("** path: ")); 711 printf(_("** path: "));
1218 const UriPathSegmentA *p = uri.pathHead; 712 for (UriPathSegmentA *path_segment = uri.pathHead; path_segment;
1219 for (; p; p = p->next) { 713 path_segment = path_segment->next) {
1220 printf("/%s", uri_string(p->text, buf, DEFAULT_BUFFER_SIZE)); 714 printf("/%s", uri_string(path_segment->text, buf, DEFAULT_BUFFER_SIZE));
1221 } 715 }
1222 puts(""); 716 puts("");
1223 } 717 }
@@ -1230,99 +724,104 @@ void redir(curlhelp_write_curlbuf *header_buf) {
1230 } 724 }
1231 725
1232 if (uri.scheme.first) { 726 if (uri.scheme.first) {
1233 if (!uri_strcmp(uri.scheme, "https")) 727 working_state.use_ssl = (bool)(!uri_strcmp(uri.scheme, "https"));
1234 use_ssl = true;
1235 else
1236 use_ssl = false;
1237 } 728 }
1238 729
1239 /* we do a sloppy test here only, because uriparser would have failed 730 /* we do a sloppy test here only, because uriparser would have failed
1240 * above, if the port would be invalid, we just check for MAX_PORT 731 * above, if the port would be invalid, we just check for MAX_PORT
1241 */ 732 */
733 int new_port;
1242 if (uri.portText.first) { 734 if (uri.portText.first) {
1243 new_port = atoi(uri_string(uri.portText, buf, DEFAULT_BUFFER_SIZE)); 735 new_port = atoi(uri_string(uri.portText, buf, DEFAULT_BUFFER_SIZE));
1244 } else { 736 } else {
1245 new_port = HTTP_PORT; 737 new_port = HTTP_PORT;
1246 if (use_ssl) 738 if (working_state.use_ssl) {
1247 new_port = HTTPS_PORT; 739 new_port = HTTPS_PORT;
740 }
741 }
742 if (new_port > MAX_PORT) {
743 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s\n"), MAX_PORT,
744 location);
1248 } 745 }
1249 if (new_port > MAX_PORT)
1250 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s%s\n"), MAX_PORT, location, display_html ? "</A>" : "");
1251 746
1252 /* by RFC 7231 relative URLs in Location should be taken relative to 747 /* by RFC 7231 relative URLs in Location should be taken relative to
1253 * the original URL, so we try to form a new absolute URL here 748 * the original URL, so we try to form a new absolute URL here
1254 */ 749 */
750 char *new_host;
1255 if (!uri.scheme.first && !uri.hostText.first) { 751 if (!uri.scheme.first && !uri.hostText.first) {
1256 new_host = strdup(host_name ? host_name : server_address); 752 new_host = strdup(working_state.host_name ? working_state.host_name
1257 new_port = server_port; 753 : working_state.server_address);
1258 if (use_ssl) 754 new_port = working_state.serverPort;
755 if (working_state.use_ssl) {
1259 uri_string(uri.scheme, "https", DEFAULT_BUFFER_SIZE); 756 uri_string(uri.scheme, "https", DEFAULT_BUFFER_SIZE);
757 }
1260 } else { 758 } else {
1261 new_host = strdup(uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE)); 759 new_host = strdup(uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE));
1262 } 760 }
1263 761
1264 /* compose new path */ 762 /* compose new path */
1265 /* TODO: handle fragments and query part of URL */ 763 /* TODO: handle fragments and query part of URL */
1266 new_url = (char *)calloc(1, DEFAULT_BUFFER_SIZE); 764 char *new_url = (char *)calloc(1, DEFAULT_BUFFER_SIZE);
1267 if (uri.pathHead) { 765 if (uri.pathHead) {
1268 const UriPathSegmentA *p = uri.pathHead; 766 for (UriPathSegmentA *pathSegment = uri.pathHead; pathSegment;
1269 for (; p; p = p->next) { 767 pathSegment = pathSegment->next) {
1270 strncat(new_url, "/", DEFAULT_BUFFER_SIZE); 768 strncat(new_url, "/", DEFAULT_BUFFER_SIZE);
1271 strncat(new_url, uri_string(p->text, buf, DEFAULT_BUFFER_SIZE), DEFAULT_BUFFER_SIZE - 1); 769 strncat(new_url, uri_string(pathSegment->text, buf, DEFAULT_BUFFER_SIZE),
770 DEFAULT_BUFFER_SIZE - 1);
1272 } 771 }
1273 } 772 }
1274 773
1275 if (server_port == new_port && !strncmp(server_address, new_host, MAX_IPV4_HOSTLENGTH) && 774 if (working_state.serverPort == new_port &&
1276 (host_name && !strncmp(host_name, new_host, MAX_IPV4_HOSTLENGTH)) && !strcmp(server_url, new_url)) 775 !strncmp(working_state.server_address, new_host, MAX_IPV4_HOSTLENGTH) &&
1277 die(STATE_CRITICAL, _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), use_ssl ? "https" : "http", 776 (working_state.host_name &&
1278 new_host, new_port, new_url, (display_html ? "</A>" : "")); 777 !strncmp(working_state.host_name, new_host, MAX_IPV4_HOSTLENGTH)) &&
778 !strcmp(working_state.server_url, new_url)) {
779 die(STATE_CRITICAL,
780 _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s\n"),
781 working_state.use_ssl ? "https" : "http", new_host, new_port, new_url);
782 }
1279 783
1280 /* set new values for redirected request */ 784 /* set new values for redirected request */
1281 785
1282 if (!(followsticky & STICKY_HOST)) { 786 if (!(config.followsticky & STICKY_HOST)) {
1283 free(server_address); 787 free(working_state.server_address);
1284 server_address = strndup(new_host, MAX_IPV4_HOSTLENGTH); 788 working_state.server_address = strndup(new_host, MAX_IPV4_HOSTLENGTH);
1285 } 789 }
1286 if (!(followsticky & STICKY_PORT)) { 790 if (!(config.followsticky & STICKY_PORT)) {
1287 server_port = (unsigned short)new_port; 791 working_state.serverPort = (unsigned short)new_port;
1288 } 792 }
1289 793
1290 free(host_name); 794 free(working_state.host_name);
1291 host_name = strndup(new_host, MAX_IPV4_HOSTLENGTH); 795 working_state.host_name = strndup(new_host, MAX_IPV4_HOSTLENGTH);
1292 796
1293 /* reset virtual port */ 797 /* reset virtual port */
1294 virtual_port = server_port; 798 working_state.virtualPort = working_state.serverPort;
1295 799
1296 free(new_host); 800 free(new_host);
1297 free(server_url); 801 free(working_state.server_url);
1298 server_url = new_url; 802 working_state.server_url = new_url;
1299 803
1300 uriFreeUriMembersA(&uri); 804 uriFreeUriMembersA(&uri);
1301 805
1302 if (verbose) 806 if (verbose) {
1303 printf(_("Redirection to %s://%s:%d%s\n"), use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, 807 printf(_("Redirection to %s://%s:%d%s\n"), working_state.use_ssl ? "https" : "http",
1304 server_url); 808 working_state.host_name ? working_state.host_name : working_state.server_address,
809 working_state.serverPort, working_state.server_url);
810 }
1305 811
1306 /* TODO: the hash component MUST be taken from the original URL and 812 /* TODO: the hash component MUST be taken from the original URL and
1307 * attached to the URL in Location 813 * attached to the URL in Location
1308 */ 814 */
1309 815
1310 cleanup(); 816 redir_wrapper result = {
1311 check_http(); 817 .redir_depth = redir_depth,
1312} 818 .working_state = working_state,
1313 819 .error_code = OK,
1314/* check whether a file exists */ 820 };
1315void test_file(char *path) { 821 return result;
1316 if (access(path, R_OK) == 0)
1317 return;
1318 usage2(_("file does not exist or is not readable"), path);
1319} 822}
1320 823
1321bool process_arguments(int argc, char **argv) { 824check_curl_config_wrapper process_arguments(int argc, char **argv) {
1322 char *p;
1323 int c = 1;
1324 char *temp;
1325
1326 enum { 825 enum {
1327 INVERT_REGEX = CHAR_MAX + 1, 826 INVERT_REGEX = CHAR_MAX + 1,
1328 SNI_OPTION, 827 SNI_OPTION,
@@ -1333,81 +832,101 @@ bool process_arguments(int argc, char **argv) {
1333 AUTOMATIC_DECOMPRESSION, 832 AUTOMATIC_DECOMPRESSION,
1334 COOKIE_JAR, 833 COOKIE_JAR,
1335 HAPROXY_PROTOCOL, 834 HAPROXY_PROTOCOL,
1336 STATE_REGEX 835 STATE_REGEX,
836 OUTPUT_FORMAT
1337 }; 837 };
1338 838
1339 int option = 0; 839 static struct option longopts[] = {
1340 int got_plus = 0; 840 STD_LONG_OPTS,
1341 static struct option longopts[] = {STD_LONG_OPTS, 841 {"link", no_argument, 0, 'L'},
1342 {"link", no_argument, 0, 'L'}, 842 {"nohtml", no_argument, 0, 'n'},
1343 {"nohtml", no_argument, 0, 'n'}, 843 {"ssl", optional_argument, 0, 'S'},
1344 {"ssl", optional_argument, 0, 'S'}, 844 {"sni", no_argument, 0, SNI_OPTION},
1345 {"sni", no_argument, 0, SNI_OPTION}, 845 {"post", required_argument, 0, 'P'},
1346 {"post", required_argument, 0, 'P'}, 846 {"method", required_argument, 0, 'j'},
1347 {"method", required_argument, 0, 'j'}, 847 {"IP-address", required_argument, 0, 'I'},
1348 {"IP-address", required_argument, 0, 'I'}, 848 {"url", required_argument, 0, 'u'},
1349 {"url", required_argument, 0, 'u'}, 849 {"port", required_argument, 0, 'p'},
1350 {"port", required_argument, 0, 'p'}, 850 {"authorization", required_argument, 0, 'a'},
1351 {"authorization", required_argument, 0, 'a'}, 851 {"proxy-authorization", required_argument, 0, 'b'},
1352 {"proxy-authorization", required_argument, 0, 'b'}, 852 {"header-string", required_argument, 0, 'd'},
1353 {"header-string", required_argument, 0, 'd'}, 853 {"string", required_argument, 0, 's'},
1354 {"string", required_argument, 0, 's'}, 854 {"expect", required_argument, 0, 'e'},
1355 {"expect", required_argument, 0, 'e'}, 855 {"regex", required_argument, 0, 'r'},
1356 {"regex", required_argument, 0, 'r'}, 856 {"ereg", required_argument, 0, 'r'},
1357 {"ereg", required_argument, 0, 'r'}, 857 {"eregi", required_argument, 0, 'R'},
1358 {"eregi", required_argument, 0, 'R'}, 858 {"linespan", no_argument, 0, 'l'},
1359 {"linespan", no_argument, 0, 'l'}, 859 {"onredirect", required_argument, 0, 'f'},
1360 {"onredirect", required_argument, 0, 'f'}, 860 {"certificate", required_argument, 0, 'C'},
1361 {"certificate", required_argument, 0, 'C'}, 861 {"client-cert", required_argument, 0, 'J'},
1362 {"client-cert", required_argument, 0, 'J'}, 862 {"private-key", required_argument, 0, 'K'},
1363 {"private-key", required_argument, 0, 'K'}, 863 {"ca-cert", required_argument, 0, CA_CERT_OPTION},
1364 {"ca-cert", required_argument, 0, CA_CERT_OPTION}, 864 {"verify-cert", no_argument, 0, 'D'},
1365 {"verify-cert", no_argument, 0, 'D'}, 865 {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT},
1366 {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT}, 866 {"useragent", required_argument, 0, 'A'},
1367 {"useragent", required_argument, 0, 'A'}, 867 {"header", required_argument, 0, 'k'},
1368 {"header", required_argument, 0, 'k'}, 868 {"no-body", no_argument, 0, 'N'},
1369 {"no-body", no_argument, 0, 'N'}, 869 {"max-age", required_argument, 0, 'M'},
1370 {"max-age", required_argument, 0, 'M'}, 870 {"content-type", required_argument, 0, 'T'},
1371 {"content-type", required_argument, 0, 'T'}, 871 {"pagesize", required_argument, 0, 'm'},
1372 {"pagesize", required_argument, 0, 'm'}, 872 {"invert-regex", no_argument, NULL, INVERT_REGEX},
1373 {"invert-regex", no_argument, NULL, INVERT_REGEX}, 873 {"state-regex", required_argument, 0, STATE_REGEX},
1374 {"state-regex", required_argument, 0, STATE_REGEX}, 874 {"use-ipv4", no_argument, 0, '4'},
1375 {"use-ipv4", no_argument, 0, '4'}, 875 {"use-ipv6", no_argument, 0, '6'},
1376 {"use-ipv6", no_argument, 0, '6'}, 876 {"extended-perfdata", no_argument, 0, 'E'},
1377 {"extended-perfdata", no_argument, 0, 'E'}, 877 {"show-body", no_argument, 0, 'B'},
1378 {"show-body", no_argument, 0, 'B'}, 878 {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION},
1379 {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION}, 879 {"http-version", required_argument, 0, HTTP_VERSION_OPTION},
1380 {"http-version", required_argument, 0, HTTP_VERSION_OPTION}, 880 {"enable-automatic-decompression", no_argument, 0, AUTOMATIC_DECOMPRESSION},
1381 {"enable-automatic-decompression", no_argument, 0, AUTOMATIC_DECOMPRESSION}, 881 {"cookie-jar", required_argument, 0, COOKIE_JAR},
1382 {"cookie-jar", required_argument, 0, COOKIE_JAR}, 882 {"haproxy-protocol", no_argument, 0, HAPROXY_PROTOCOL},
1383 {"haproxy-protocol", no_argument, 0, HAPROXY_PROTOCOL}, 883 {"output-format", required_argument, 0, OUTPUT_FORMAT},
1384 {0, 0, 0, 0}}; 884 {0, 0, 0, 0}};
1385 885
1386 if (argc < 2) 886 check_curl_config_wrapper result = {
1387 return false; 887 .errorcode = OK,
888 .config = check_curl_config_init(),
889 };
1388 890
1389 /* support check_http compatible arguments */ 891 if (argc < 2) {
1390 for (c = 1; c < argc; c++) { 892 result.errorcode = ERROR;
1391 if (strcmp("-to", argv[c]) == 0) 893 return result;
1392 strcpy(argv[c], "-t");
1393 if (strcmp("-hn", argv[c]) == 0)
1394 strcpy(argv[c], "-H");
1395 if (strcmp("-wt", argv[c]) == 0)
1396 strcpy(argv[c], "-w");
1397 if (strcmp("-ct", argv[c]) == 0)
1398 strcpy(argv[c], "-c");
1399 if (strcmp("-nohtml", argv[c]) == 0)
1400 strcpy(argv[c], "-n");
1401 } 894 }
1402 895
1403 server_url = strdup(DEFAULT_SERVER_URL); 896 /* support check_http compatible arguments */
897 for (int index = 1; index < argc; index++) {
898 if (strcmp("-to", argv[index]) == 0) {
899 strcpy(argv[index], "-t");
900 }
901 if (strcmp("-hn", argv[index]) == 0) {
902 strcpy(argv[index], "-H");
903 }
904 if (strcmp("-wt", argv[index]) == 0) {
905 strcpy(argv[index], "-w");
906 }
907 if (strcmp("-ct", argv[index]) == 0) {
908 strcpy(argv[index], "-c");
909 }
910 if (strcmp("-nohtml", argv[index]) == 0) {
911 strcpy(argv[index], "-n");
912 }
913 }
1404 914
1405 while (1) { 915 int option = 0;
1406 c = getopt_long(argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:DnlLS::m:M:NEB", longopts, &option); 916 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
1407 if (c == -1 || c == EOF || c == 1) 917 bool specify_port = false;
918 bool enable_tls = false;
919 char *tls_option_optarg = NULL;
920
921 while (true) {
922 int option_index = getopt_long(
923 argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:DnlLS::m:M:NEB",
924 longopts, &option);
925 if (option_index == -1 || option_index == EOF || option_index == 1) {
1408 break; 926 break;
927 }
1409 928
1410 switch (c) { 929 switch (option_index) {
1411 case 'h': 930 case 'h':
1412 print_help(); 931 print_help();
1413 exit(STATE_UNKNOWN); 932 exit(STATE_UNKNOWN);
@@ -1421,270 +940,253 @@ bool process_arguments(int argc, char **argv) {
1421 verbose++; 940 verbose++;
1422 break; 941 break;
1423 case 't': /* timeout period */ 942 case 't': /* timeout period */
1424 if (!is_intnonneg(optarg)) 943 if (!is_intnonneg(optarg)) {
1425 usage2(_("Timeout interval must be a positive integer"), optarg); 944 usage2(_("Timeout interval must be a positive integer"), optarg);
1426 else 945 } else {
1427 socket_timeout = (int)strtol(optarg, NULL, 10); 946 result.config.curl_config.socket_timeout = (int)strtol(optarg, NULL, 10);
947 }
1428 break; 948 break;
1429 case 'c': /* critical time threshold */ 949 case 'c': /* critical time threshold */
1430 critical_thresholds = optarg; 950 {
1431 break; 951 mp_range_parsed critical_range = mp_parse_range_string(optarg);
952 if (critical_range.error != MP_PARSING_SUCCES) {
953 die(STATE_UNKNOWN, "failed to parse critical threshold: %s", optarg);
954 }
955 result.config.thlds = mp_thresholds_set_crit(result.config.thlds, critical_range.range);
956 } break;
1432 case 'w': /* warning time threshold */ 957 case 'w': /* warning time threshold */
1433 warning_thresholds = optarg; 958 {
1434 break; 959 mp_range_parsed warning_range = mp_parse_range_string(optarg);
960
961 if (warning_range.error != MP_PARSING_SUCCES) {
962 die(STATE_UNKNOWN, "failed to parse warning threshold: %s", optarg);
963 }
964 result.config.thlds = mp_thresholds_set_warn(result.config.thlds, warning_range.range);
965 } break;
1435 case 'H': /* virtual host */ 966 case 'H': /* virtual host */
1436 host_name = strdup(optarg); 967 result.config.initial_config.host_name = strdup(optarg);
1437 if (host_name[0] == '[') { 968 char *tmp_string;
1438 if ((p = strstr(host_name, "]:")) != NULL) { /* [IPv6]:port */ 969 size_t host_name_length;
1439 virtual_port = atoi(p + 2); 970 if (result.config.initial_config.host_name[0] == '[') {
971 if ((tmp_string = strstr(result.config.initial_config.host_name, "]:")) !=
972 NULL) { /* [IPv6]:port */
973 result.config.initial_config.virtualPort = atoi(tmp_string + 2);
1440 /* cut off the port */ 974 /* cut off the port */
1441 host_name_length = strlen(host_name) - strlen(p) - 1; 975 host_name_length =
1442 free(host_name); 976 strlen(result.config.initial_config.host_name) - strlen(tmp_string) - 1;
1443 host_name = strndup(optarg, host_name_length); 977 free(result.config.initial_config.host_name);
978 result.config.initial_config.host_name = strndup(optarg, host_name_length);
1444 } 979 }
1445 } else if ((p = strchr(host_name, ':')) != NULL && strchr(++p, ':') == NULL) { /* IPv4:port or host:port */ 980 } else if ((tmp_string = strchr(result.config.initial_config.host_name, ':')) != NULL &&
1446 virtual_port = atoi(p); 981 strchr(++tmp_string, ':') == NULL) { /* IPv4:port or host:port */
982 result.config.initial_config.virtualPort = atoi(tmp_string);
1447 /* cut off the port */ 983 /* cut off the port */
1448 host_name_length = strlen(host_name) - strlen(p) - 1; 984 host_name_length =
1449 free(host_name); 985 strlen(result.config.initial_config.host_name) - strlen(tmp_string) - 1;
1450 host_name = strndup(optarg, host_name_length); 986 free(result.config.initial_config.host_name);
987 result.config.initial_config.host_name = strndup(optarg, host_name_length);
1451 } 988 }
1452 break; 989 break;
1453 case 'I': /* internet address */ 990 case 'I': /* internet address */
1454 server_address = strdup(optarg); 991 result.config.initial_config.server_address = strdup(optarg);
1455 break; 992 break;
1456 case 'u': /* URL path */ 993 case 'u': /* URL path */
1457 server_url = strdup(optarg); 994 result.config.initial_config.server_url = strdup(optarg);
1458 break; 995 break;
1459 case 'p': /* Server port */ 996 case 'p': /* Server port */
1460 if (!is_intnonneg(optarg)) 997 if (!is_intnonneg(optarg)) {
1461 usage2(_("Invalid port number, expecting a non-negative number"), optarg); 998 usage2(_("Invalid port number, expecting a non-negative number"), optarg);
1462 else { 999 } else {
1463 if (strtol(optarg, NULL, 10) > MAX_PORT) 1000 if (strtol(optarg, NULL, 10) > MAX_PORT) {
1464 usage2(_("Invalid port number, supplied port number is too big"), optarg); 1001 usage2(_("Invalid port number, supplied port number is too big"), optarg);
1465 server_port = (unsigned short)strtol(optarg, NULL, 10); 1002 }
1003 result.config.initial_config.serverPort = (unsigned short)strtol(optarg, NULL, 10);
1466 specify_port = true; 1004 specify_port = true;
1467 } 1005 }
1468 break; 1006 break;
1469 case 'a': /* authorization info */ 1007 case 'a': /* authorization info */
1470 strncpy(user_auth, optarg, MAX_INPUT_BUFFER - 1); 1008 strncpy(result.config.curl_config.user_auth, optarg, MAX_INPUT_BUFFER - 1);
1471 user_auth[MAX_INPUT_BUFFER - 1] = 0; 1009 result.config.curl_config.user_auth[MAX_INPUT_BUFFER - 1] = 0;
1472 break; 1010 break;
1473 case 'b': /* proxy-authorization info */ 1011 case 'b': /* proxy-authorization info */
1474 strncpy(proxy_auth, optarg, MAX_INPUT_BUFFER - 1); 1012 strncpy(result.config.curl_config.proxy_auth, optarg, MAX_INPUT_BUFFER - 1);
1475 proxy_auth[MAX_INPUT_BUFFER - 1] = 0; 1013 result.config.curl_config.proxy_auth[MAX_INPUT_BUFFER - 1] = 0;
1476 break; 1014 break;
1477 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */ 1015 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
1478 if (!http_post_data) 1016 if (!result.config.initial_config.http_post_data) {
1479 http_post_data = strdup(optarg); 1017 result.config.initial_config.http_post_data = strdup(optarg);
1480 if (!http_method) 1018 }
1481 http_method = strdup("POST"); 1019 if (!result.config.initial_config.http_method) {
1020 result.config.initial_config.http_method = strdup("POST");
1021 }
1482 break; 1022 break;
1483 case 'j': /* Set HTTP method */ 1023 case 'j': /* Set HTTP method */
1484 if (http_method) 1024 if (result.config.initial_config.http_method) {
1485 free(http_method); 1025 free(result.config.initial_config.http_method);
1486 http_method = strdup(optarg); 1026 }
1027 result.config.initial_config.http_method = strdup(optarg);
1487 break; 1028 break;
1488 case 'A': /* useragent */ 1029 case 'A': /* useragent */
1489 strncpy(user_agent, optarg, DEFAULT_BUFFER_SIZE); 1030 strncpy(result.config.curl_config.user_agent, optarg, DEFAULT_BUFFER_SIZE);
1490 user_agent[DEFAULT_BUFFER_SIZE - 1] = '\0'; 1031 result.config.curl_config.user_agent[DEFAULT_BUFFER_SIZE - 1] = '\0';
1491 break; 1032 break;
1492 case 'k': /* Additional headers */ 1033 case 'k': /* Additional headers */
1493 if (http_opt_headers_count == 0) 1034 if (result.config.curl_config.http_opt_headers_count == 0) {
1494 http_opt_headers = malloc(sizeof(char *) * (++http_opt_headers_count)); 1035 result.config.curl_config.http_opt_headers =
1495 else 1036 malloc(sizeof(char *) * (++result.config.curl_config.http_opt_headers_count));
1496 http_opt_headers = realloc(http_opt_headers, sizeof(char *) * (++http_opt_headers_count)); 1037 } else {
1497 http_opt_headers[http_opt_headers_count - 1] = optarg; 1038 result.config.curl_config.http_opt_headers =
1039 realloc(result.config.curl_config.http_opt_headers,
1040 sizeof(char *) * (++result.config.curl_config.http_opt_headers_count));
1041 }
1042 result.config.curl_config
1043 .http_opt_headers[result.config.curl_config.http_opt_headers_count - 1] = optarg;
1498 break; 1044 break;
1499 case 'L': /* show html link */ 1045 case 'L': /* show html link */
1500 display_html = true;
1501 break;
1502 case 'n': /* do not show html link */ 1046 case 'n': /* do not show html link */
1503 display_html = false; 1047 // HTML link related options are deprecated
1504 break; 1048 break;
1505 case 'C': /* Check SSL cert validity */ 1049 case 'C': /* Check SSL cert validity */
1506#ifdef LIBCURL_FEATURE_SSL 1050#ifndef LIBCURL_FEATURE_SSL
1507 if ((temp = strchr(optarg, ',')) != NULL) { 1051 usage4(_("Invalid option - SSL is not available"));
1508 *temp = '\0';
1509 if (!is_intnonneg(optarg))
1510 usage2(_("Invalid certificate expiration period"), optarg);
1511 days_till_exp_warn = atoi(optarg);
1512 *temp = ',';
1513 temp++;
1514 if (!is_intnonneg(temp))
1515 usage2(_("Invalid certificate expiration period"), temp);
1516 days_till_exp_crit = atoi(temp);
1517 } else {
1518 days_till_exp_crit = 0;
1519 if (!is_intnonneg(optarg))
1520 usage2(_("Invalid certificate expiration period"), optarg);
1521 days_till_exp_warn = atoi(optarg);
1522 }
1523 check_cert = true;
1524 goto enable_ssl;
1525#endif 1052#endif
1053 {
1054 char *temp;
1055 if ((temp = strchr(optarg, ',')) != NULL) {
1056 *temp = '\0';
1057 if (!is_intnonneg(optarg)) {
1058 usage2(_("Invalid certificate expiration period"), optarg);
1059 }
1060 result.config.days_till_exp_warn = atoi(optarg);
1061 *temp = ',';
1062 temp++;
1063 if (!is_intnonneg(temp)) {
1064 usage2(_("Invalid certificate expiration period"), temp);
1065 }
1066 result.config.days_till_exp_crit = atoi(temp);
1067 } else {
1068 result.config.days_till_exp_crit = 0;
1069 if (!is_intnonneg(optarg)) {
1070 usage2(_("Invalid certificate expiration period"), optarg);
1071 }
1072 result.config.days_till_exp_warn = atoi(optarg);
1073 }
1074 result.config.check_cert = true;
1075 enable_tls = true;
1076 }
1077 break;
1526 case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */ 1078 case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */
1527#ifdef HAVE_SSL 1079#ifdef HAVE_SSL
1528 continue_after_check_cert = true; 1080 result.config.continue_after_check_cert = true;
1529 break; 1081 break;
1530#endif 1082#endif
1531 case 'J': /* use client certificate */ 1083 case 'J': /* use client certificate */
1532#ifdef LIBCURL_FEATURE_SSL 1084#ifndef LIBCURL_FEATURE_SSL
1533 test_file(optarg); 1085 usage4(_("Invalid option - SSL is not available"));
1534 client_cert = optarg;
1535 goto enable_ssl;
1536#endif 1086#endif
1537 case 'K': /* use client private key */
1538#ifdef LIBCURL_FEATURE_SSL
1539 test_file(optarg); 1087 test_file(optarg);
1540 client_privkey = optarg; 1088 result.config.curl_config.client_cert = optarg;
1541 goto enable_ssl; 1089 enable_tls = true;
1090 break;
1091 case 'K': /* use client private key */
1092#ifndef LIBCURL_FEATURE_SSL
1093 usage4(_("Invalid option - SSL is not available"));
1542#endif 1094#endif
1543#ifdef LIBCURL_FEATURE_SSL
1544 case CA_CERT_OPTION: /* use CA chain file */
1545 test_file(optarg); 1095 test_file(optarg);
1546 ca_cert = optarg; 1096 result.config.curl_config.client_privkey = optarg;
1547 goto enable_ssl; 1097 enable_tls = true;
1098 break;
1099 case CA_CERT_OPTION: /* use CA chain file */
1100#ifndef LIBCURL_FEATURE_SSL
1101 usage4(_("Invalid option - SSL is not available"));
1548#endif 1102#endif
1549#ifdef LIBCURL_FEATURE_SSL 1103 test_file(optarg);
1550 case 'D': /* verify peer certificate & host */ 1104 result.config.curl_config.ca_cert = optarg;
1551 verify_peer_and_host = true; 1105 enable_tls = true;
1552 break; 1106 break;
1107 case 'D': /* verify peer certificate & host */
1108#ifndef LIBCURL_FEATURE_SSL
1109 usage4(_("Invalid option - SSL is not available"));
1553#endif 1110#endif
1554 case 'S': /* use SSL */ 1111 result.config.curl_config.verify_peer_and_host = true;
1555#ifdef LIBCURL_FEATURE_SSL 1112 enable_tls = true;
1556 enable_ssl:
1557 use_ssl = true;
1558 /* ssl_version initialized to CURL_SSLVERSION_DEFAULT as a default.
1559 * Only set if it's non-zero. This helps when we include multiple
1560 * parameters, like -S and -C combinations */
1561 ssl_version = CURL_SSLVERSION_DEFAULT;
1562 if (c == 'S' && optarg != NULL) {
1563 char *plus_ptr = strchr(optarg, '+');
1564 if (plus_ptr) {
1565 got_plus = 1;
1566 *plus_ptr = '\0';
1567 }
1568
1569 if (optarg[0] == '2')
1570 ssl_version = CURL_SSLVERSION_SSLv2;
1571 else if (optarg[0] == '3')
1572 ssl_version = CURL_SSLVERSION_SSLv3;
1573 else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0"))
1574# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1575 ssl_version = CURL_SSLVERSION_TLSv1_0;
1576# else
1577 ssl_version = CURL_SSLVERSION_DEFAULT;
1578# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1579 else if (!strcmp(optarg, "1.1"))
1580# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1581 ssl_version = CURL_SSLVERSION_TLSv1_1;
1582# else
1583 ssl_version = CURL_SSLVERSION_DEFAULT;
1584# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1585 else if (!strcmp(optarg, "1.2"))
1586# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1587 ssl_version = CURL_SSLVERSION_TLSv1_2;
1588# else
1589 ssl_version = CURL_SSLVERSION_DEFAULT;
1590# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1591 else if (!strcmp(optarg, "1.3"))
1592# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0)
1593 ssl_version = CURL_SSLVERSION_TLSv1_3;
1594# else
1595 ssl_version = CURL_SSLVERSION_DEFAULT;
1596# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */
1597 else
1598 usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 (with optional '+' suffix)"));
1599 }
1600# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0)
1601 if (got_plus) {
1602 switch (ssl_version) {
1603 case CURL_SSLVERSION_TLSv1_3:
1604 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1605 break;
1606 case CURL_SSLVERSION_TLSv1_2:
1607 case CURL_SSLVERSION_TLSv1_1:
1608 case CURL_SSLVERSION_TLSv1_0:
1609 ssl_version |= CURL_SSLVERSION_MAX_DEFAULT;
1610 break;
1611 }
1612 } else {
1613 switch (ssl_version) {
1614 case CURL_SSLVERSION_TLSv1_3:
1615 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1616 break;
1617 case CURL_SSLVERSION_TLSv1_2:
1618 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2;
1619 break;
1620 case CURL_SSLVERSION_TLSv1_1:
1621 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1;
1622 break;
1623 case CURL_SSLVERSION_TLSv1_0:
1624 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0;
1625 break;
1626 }
1627 }
1628# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */
1629 if (verbose >= 2)
1630 printf(_("* Set SSL/TLS version to %d\n"), ssl_version);
1631 if (!specify_port)
1632 server_port = HTTPS_PORT;
1633 break; 1113 break;
1634#else /* LIBCURL_FEATURE_SSL */ 1114 case 'S': /* use SSL */
1635 /* -C -J and -K fall through to here without SSL */ 1115 tls_option_optarg = optarg;
1116 enable_tls = true;
1117#ifndef LIBCURL_FEATURE_SSL
1636 usage4(_("Invalid option - SSL is not available")); 1118 usage4(_("Invalid option - SSL is not available"));
1119#endif
1637 break; 1120 break;
1638 case SNI_OPTION: /* --sni is parsed, but ignored, the default is true with libcurl */ 1121 case SNI_OPTION: /* --sni is parsed, but ignored, the default is true with libcurl */
1639 use_sni = true; 1122#ifndef LIBCURL_FEATURE_SSL
1640 break; 1123 usage4(_("Invalid option - SSL is not available"));
1641#endif /* LIBCURL_FEATURE_SSL */ 1124#endif /* LIBCURL_FEATURE_SSL */
1125 break;
1642 case MAX_REDIRS_OPTION: 1126 case MAX_REDIRS_OPTION:
1643 if (!is_intnonneg(optarg)) 1127 if (!is_intnonneg(optarg)) {
1644 usage2(_("Invalid max_redirs count"), optarg); 1128 usage2(_("Invalid max_redirs count"), optarg);
1645 else { 1129 } else {
1646 max_depth = atoi(optarg); 1130 result.config.max_depth = atoi(optarg);
1647 } 1131 }
1648 break; 1132 break;
1649 case 'f': /* onredirect */ 1133 case 'f': /* onredirect */
1650 if (!strcmp(optarg, "ok")) 1134 if (!strcmp(optarg, "ok")) {
1651 onredirect = STATE_OK; 1135 result.config.on_redirect_result_state = STATE_OK;
1652 else if (!strcmp(optarg, "warning")) 1136 result.config.on_redirect_dependent = false;
1653 onredirect = STATE_WARNING; 1137 } else if (!strcmp(optarg, "warning")) {
1654 else if (!strcmp(optarg, "critical")) 1138 result.config.on_redirect_result_state = STATE_WARNING;
1655 onredirect = STATE_CRITICAL; 1139 result.config.on_redirect_dependent = false;
1656 else if (!strcmp(optarg, "unknown")) 1140 } else if (!strcmp(optarg, "critical")) {
1657 onredirect = STATE_UNKNOWN; 1141 result.config.on_redirect_result_state = STATE_CRITICAL;
1658 else if (!strcmp(optarg, "follow")) 1142 result.config.on_redirect_dependent = false;
1659 onredirect = STATE_DEPENDENT; 1143 } else if (!strcmp(optarg, "unknown")) {
1660 else if (!strcmp(optarg, "stickyport")) 1144 result.config.on_redirect_result_state = STATE_UNKNOWN;
1661 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST | STICKY_PORT; 1145 result.config.on_redirect_dependent = false;
1662 else if (!strcmp(optarg, "sticky")) 1146 } else if (!strcmp(optarg, "follow")) {
1663 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST; 1147 result.config.on_redirect_dependent = true;
1664 else if (!strcmp(optarg, "follow")) 1148 } else if (!strcmp(optarg, "stickyport")) {
1665 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_NONE; 1149 result.config.on_redirect_dependent = true;
1666 else if (!strcmp(optarg, "curl")) 1150 result.config.followmethod = FOLLOW_HTTP_CURL,
1667 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_LIBCURL; 1151 result.config.followsticky = STICKY_HOST | STICKY_PORT;
1668 else 1152 } else if (!strcmp(optarg, "sticky")) {
1153 result.config.on_redirect_dependent = true;
1154 result.config.followmethod = FOLLOW_HTTP_CURL,
1155 result.config.followsticky = STICKY_HOST;
1156 } else if (!strcmp(optarg, "follow")) {
1157 result.config.on_redirect_dependent = true;
1158 result.config.followmethod = FOLLOW_HTTP_CURL,
1159 result.config.followsticky = STICKY_NONE;
1160 } else if (!strcmp(optarg, "curl")) {
1161 result.config.on_redirect_dependent = true;
1162 result.config.followmethod = FOLLOW_LIBCURL;
1163 } else {
1669 usage2(_("Invalid onredirect option"), optarg); 1164 usage2(_("Invalid onredirect option"), optarg);
1670 if (verbose >= 2) 1165 }
1671 printf(_("* Following redirects set to %s\n"), state_text(onredirect)); 1166 if (verbose >= 2) {
1167 if (result.config.on_redirect_dependent) {
1168 printf(_("* Following redirects\n"));
1169 } else {
1170 printf(_("* Following redirects set to state %s\n"),
1171 state_text(result.config.on_redirect_result_state));
1172 }
1173 }
1672 break; 1174 break;
1673 case 'd': /* string or substring */ 1175 case 'd': /* string or substring */
1674 strncpy(header_expect, optarg, MAX_INPUT_BUFFER - 1); 1176 strncpy(result.config.header_expect, optarg, MAX_INPUT_BUFFER - 1);
1675 header_expect[MAX_INPUT_BUFFER - 1] = 0; 1177 result.config.header_expect[MAX_INPUT_BUFFER - 1] = 0;
1676 break; 1178 break;
1677 case 's': /* string or substring */ 1179 case 's': /* string or substring */
1678 strncpy(string_expect, optarg, MAX_INPUT_BUFFER - 1); 1180 strncpy(result.config.string_expect, optarg, MAX_INPUT_BUFFER - 1);
1679 string_expect[MAX_INPUT_BUFFER - 1] = 0; 1181 result.config.string_expect[MAX_INPUT_BUFFER - 1] = 0;
1680 break; 1182 break;
1681 case 'e': /* string or substring */ 1183 case 'e': /* string or substring */
1682 strncpy(server_expect, optarg, MAX_INPUT_BUFFER - 1); 1184 strncpy(result.config.server_expect.string, optarg, MAX_INPUT_BUFFER - 1);
1683 server_expect[MAX_INPUT_BUFFER - 1] = 0; 1185 result.config.server_expect.string[MAX_INPUT_BUFFER - 1] = 0;
1684 server_expect_yn = 1; 1186 result.config.server_expect.is_present = true;
1685 break; 1187 break;
1686 case 'T': /* Content-type */ 1188 case 'T': /* Content-type */
1687 http_content_type = strdup(optarg); 1189 result.config.curl_config.http_content_type = strdup(optarg);
1688 break; 1190 break;
1689 case 'l': /* linespan */ 1191 case 'l': /* linespan */
1690 cflags &= ~REG_NEWLINE; 1192 cflags &= ~REG_NEWLINE;
@@ -1693,185 +1195,258 @@ bool process_arguments(int argc, char **argv) {
1693 cflags |= REG_ICASE; 1195 cflags |= REG_ICASE;
1694 // fall through 1196 // fall through
1695 case 'r': /* regex */ 1197 case 'r': /* regex */
1696 strncpy(regexp, optarg, MAX_RE_SIZE - 1); 1198 strncpy(result.config.regexp, optarg, MAX_RE_SIZE - 1);
1697 regexp[MAX_RE_SIZE - 1] = 0; 1199 result.config.regexp[MAX_RE_SIZE - 1] = 0;
1698 errcode = regcomp(&preg, regexp, cflags); 1200 regex_t preg;
1201 int errcode = regcomp(&preg, result.config.regexp, cflags);
1699 if (errcode != 0) { 1202 if (errcode != 0) {
1700 (void)regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); 1203 (void)regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1701 printf(_("Could Not Compile Regular Expression: %s"), errbuf); 1204 printf(_("Could Not Compile Regular Expression: %s"), errbuf);
1702 return false; 1205 result.errorcode = ERROR;
1206 return result;
1703 } 1207 }
1208
1209 result.config.compiled_regex = preg;
1704 break; 1210 break;
1705 case INVERT_REGEX: 1211 case INVERT_REGEX:
1706 invert_regex = true; 1212 result.config.invert_regex = true;
1707 break; 1213 break;
1708 case STATE_REGEX: 1214 case STATE_REGEX:
1709 if (!strcasecmp(optarg, "critical")) 1215 if (!strcasecmp(optarg, "critical")) {
1710 state_regex = STATE_CRITICAL; 1216 result.config.state_regex = STATE_CRITICAL;
1711 else if (!strcasecmp(optarg, "warning")) 1217 } else if (!strcasecmp(optarg, "warning")) {
1712 state_regex = STATE_WARNING; 1218 result.config.state_regex = STATE_WARNING;
1713 else 1219 } else {
1714 usage2(_("Invalid state-regex option"), optarg); 1220 usage2(_("Invalid state-regex option"), optarg);
1221 }
1715 break; 1222 break;
1716 case '4': 1223 case '4':
1717 address_family = AF_INET; 1224 result.config.curl_config.sin_family = AF_INET;
1718 break; 1225 break;
1719 case '6': 1226 case '6':
1720#if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) 1227#if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6)
1721 address_family = AF_INET6; 1228 result.config.curl_config.sin_family = AF_INET6;
1722#else 1229#else
1723 usage4(_("IPv6 support not available")); 1230 usage4(_("IPv6 support not available"));
1724#endif 1231#endif
1725 break; 1232 break;
1726 case 'm': /* min_page_length */ 1233 case 'm': /* min_page_length */
1727 { 1234 {
1728 char *tmp; 1235 mp_range_parsed foo = mp_parse_range_string(optarg);
1729 if (strchr(optarg, ':') != (char *)NULL) { 1236
1730 /* range, so get two values, min:max */ 1237 if (foo.error != MP_PARSING_SUCCES) {
1731 tmp = strtok(optarg, ":"); 1238 die(STATE_CRITICAL, "failed to parse page size limits: %s", optarg);
1732 if (tmp == NULL) { 1239 }
1733 printf("Bad format: try \"-m min:max\"\n"); 1240
1734 exit(STATE_WARNING); 1241 result.config.page_length_limits = foo.range;
1735 } else 1242 result.config.page_length_limits_is_set = true;
1736 min_page_len = atoi(tmp);
1737
1738 tmp = strtok(NULL, ":");
1739 if (tmp == NULL) {
1740 printf("Bad format: try \"-m min:max\"\n");
1741 exit(STATE_WARNING);
1742 } else
1743 max_page_len = atoi(tmp);
1744 } else
1745 min_page_len = atoi(optarg);
1746 break; 1243 break;
1747 } 1244 }
1748 case 'N': /* no-body */ 1245 case 'N': /* no-body */
1749 no_body = true; 1246 result.config.initial_config.no_body = true;
1750 break; 1247 break;
1751 case 'M': /* max-age */ 1248 case 'M': /* max-age */
1752 { 1249 {
1753 int L = strlen(optarg); 1250 size_t option_length = strlen(optarg);
1754 if (L && optarg[L - 1] == 'm') 1251 if (option_length && optarg[option_length - 1] == 'm') {
1755 maximum_age = atoi(optarg) * 60; 1252 result.config.maximum_age = atoi(optarg) * 60;
1756 else if (L && optarg[L - 1] == 'h') 1253 } else if (option_length && optarg[option_length - 1] == 'h') {
1757 maximum_age = atoi(optarg) * 60 * 60; 1254 result.config.maximum_age = atoi(optarg) * 60 * 60;
1758 else if (L && optarg[L - 1] == 'd') 1255 } else if (option_length && optarg[option_length - 1] == 'd') {
1759 maximum_age = atoi(optarg) * 60 * 60 * 24; 1256 result.config.maximum_age = atoi(optarg) * 60 * 60 * 24;
1760 else if (L && (optarg[L - 1] == 's' || isdigit(optarg[L - 1]))) 1257 } else if (option_length &&
1761 maximum_age = atoi(optarg); 1258 (optarg[option_length - 1] == 's' || isdigit(optarg[option_length - 1]))) {
1762 else { 1259 result.config.maximum_age = atoi(optarg);
1260 } else {
1763 fprintf(stderr, "unparsable max-age: %s\n", optarg); 1261 fprintf(stderr, "unparsable max-age: %s\n", optarg);
1764 exit(STATE_WARNING); 1262 exit(STATE_WARNING);
1765 } 1263 }
1766 if (verbose >= 2) 1264 if (verbose >= 2) {
1767 printf("* Maximal age of document set to %d seconds\n", maximum_age); 1265 printf("* Maximal age of document set to %d seconds\n", result.config.maximum_age);
1266 }
1768 } break; 1267 } break;
1769 case 'E': /* show extended perfdata */ 1268 case 'E': /* show extended perfdata */
1770 show_extended_perfdata = true; 1269 result.config.show_extended_perfdata = true;
1771 break; 1270 break;
1772 case 'B': /* print body content after status line */ 1271 case 'B': /* print body content after status line */
1773 show_body = true; 1272 result.config.show_body = true;
1774 break; 1273 break;
1775 case HTTP_VERSION_OPTION: 1274 case HTTP_VERSION_OPTION:
1776 curl_http_version = CURL_HTTP_VERSION_NONE; 1275 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_NONE;
1777 if (strcmp(optarg, "1.0") == 0) { 1276 if (strcmp(optarg, "1.0") == 0) {
1778 curl_http_version = CURL_HTTP_VERSION_1_0; 1277 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_1_0;
1779 } else if (strcmp(optarg, "1.1") == 0) { 1278 } else if (strcmp(optarg, "1.1") == 0) {
1780 curl_http_version = CURL_HTTP_VERSION_1_1; 1279 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_1_1;
1781 } else if ((strcmp(optarg, "2.0") == 0) || (strcmp(optarg, "2") == 0)) { 1280 } else if ((strcmp(optarg, "2.0") == 0) || (strcmp(optarg, "2") == 0)) {
1782#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) 1281#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0)
1783 curl_http_version = CURL_HTTP_VERSION_2_0; 1282 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_2_0;
1784#else 1283#else
1785 curl_http_version = CURL_HTTP_VERSION_NONE; 1284 result.config.curl_http_version = CURL_HTTP_VERSION_NONE;
1786#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) */ 1285#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) */
1286 } else if ((strcmp(optarg, "3") == 0)) {
1287#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 66, 0)
1288 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_3;
1289#else
1290 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_NONE;
1291#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 66, 0) */
1787 } else { 1292 } else {
1788 fprintf(stderr, "unknown http-version parameter: %s\n", optarg); 1293 fprintf(stderr, "unknown http-version parameter: %s\n", optarg);
1789 exit(STATE_WARNING); 1294 exit(STATE_WARNING);
1790 } 1295 }
1791 break; 1296 break;
1792 case AUTOMATIC_DECOMPRESSION: 1297 case AUTOMATIC_DECOMPRESSION:
1793 automatic_decompression = true; 1298 result.config.curl_config.automatic_decompression = true;
1794 break; 1299 break;
1795 case COOKIE_JAR: 1300 case COOKIE_JAR:
1796 cookie_jar_file = optarg; 1301 result.config.curl_config.cookie_jar_file = optarg;
1797 break; 1302 break;
1798 case HAPROXY_PROTOCOL: 1303 case HAPROXY_PROTOCOL:
1799 haproxy_protocol = true; 1304 result.config.curl_config.haproxy_protocol = true;
1800 break; 1305 break;
1801 case '?': 1306 case '?':
1802 /* print short usage statement if args not parsable */ 1307 /* print short usage statement if args not parsable */
1803 usage5(); 1308 usage5();
1804 break; 1309 break;
1310 case OUTPUT_FORMAT: {
1311 parsed_output_format parser = mp_parse_output_format(optarg);
1312 if (!parser.parsing_success) {
1313 // TODO List all available formats here, maybe add anothoer usage function
1314 printf("Invalid output format: %s\n", optarg);
1315 exit(STATE_UNKNOWN);
1316 }
1317
1318 result.config.output_format_is_set = true;
1319 result.config.output_format = parser.output_format;
1320 break;
1321 }
1805 } 1322 }
1806 } 1323 }
1807 1324
1808 c = optind; 1325 if (enable_tls) {
1326 bool got_plus = false;
1327 result.config.initial_config.use_ssl = true;
1328 /* ssl_version initialized to CURL_SSLVERSION_DEFAULT as a default.
1329 * Only set if it's non-zero. This helps when we include multiple
1330 * parameters, like -S and -C combinations */
1331 result.config.curl_config.ssl_version = CURL_SSLVERSION_DEFAULT;
1332 if (tls_option_optarg != NULL) {
1333 char *plus_ptr = strchr(optarg, '+');
1334 if (plus_ptr) {
1335 got_plus = true;
1336 *plus_ptr = '\0';
1337 }
1809 1338
1810 if (server_address == NULL && c < argc) 1339 if (optarg[0] == '2') {
1811 server_address = strdup(argv[c++]); 1340 result.config.curl_config.ssl_version = CURL_SSLVERSION_SSLv2;
1341 } else if (optarg[0] == '3') {
1342 result.config.curl_config.ssl_version = CURL_SSLVERSION_SSLv3;
1343 } else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0")) {
1344#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1345 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_0;
1346#else
1347 result.config.ssl_version = CURL_SSLVERSION_DEFAULT;
1348#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1349 } else if (!strcmp(optarg, "1.1")) {
1350#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1351 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_1;
1352#else
1353 result.config.ssl_version = CURL_SSLVERSION_DEFAULT;
1354#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1355 } else if (!strcmp(optarg, "1.2")) {
1356#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1357 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_2;
1358#else
1359 result.config.ssl_version = CURL_SSLVERSION_DEFAULT;
1360#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1361 } else if (!strcmp(optarg, "1.3")) {
1362#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0)
1363 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_3;
1364#else
1365 result.config.ssl_version = CURL_SSLVERSION_DEFAULT;
1366#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */
1367 } else {
1368 usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 "
1369 "(with optional '+' suffix)"));
1370 }
1371 }
1372#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0)
1373 if (got_plus) {
1374 switch (result.config.curl_config.ssl_version) {
1375 case CURL_SSLVERSION_TLSv1_3:
1376 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1377 break;
1378 case CURL_SSLVERSION_TLSv1_2:
1379 case CURL_SSLVERSION_TLSv1_1:
1380 case CURL_SSLVERSION_TLSv1_0:
1381 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_DEFAULT;
1382 break;
1383 }
1384 } else {
1385 switch (result.config.curl_config.ssl_version) {
1386 case CURL_SSLVERSION_TLSv1_3:
1387 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1388 break;
1389 case CURL_SSLVERSION_TLSv1_2:
1390 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2;
1391 break;
1392 case CURL_SSLVERSION_TLSv1_1:
1393 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1;
1394 break;
1395 case CURL_SSLVERSION_TLSv1_0:
1396 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0;
1397 break;
1398 }
1399 }
1400#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */
1401 if (verbose >= 2) {
1402 printf(_("* Set SSL/TLS version to %d\n"), result.config.curl_config.ssl_version);
1403 }
1404 if (!specify_port) {
1405 result.config.initial_config.serverPort = HTTPS_PORT;
1406 }
1407 }
1812 1408
1813 if (host_name == NULL && c < argc) 1409 int option_counter = optind;
1814 host_name = strdup(argv[c++]);
1815 1410
1816 if (server_address == NULL) { 1411 if (result.config.initial_config.server_address == NULL && option_counter < argc) {
1817 if (host_name == NULL) 1412 result.config.initial_config.server_address = strdup(argv[option_counter++]);
1818 usage4(_("You must specify a server address or host name"));
1819 else
1820 server_address = strdup(host_name);
1821 } 1413 }
1822 1414
1823 set_thresholds(&thlds, warning_thresholds, critical_thresholds); 1415 if (result.config.initial_config.host_name == NULL && option_counter < argc) {
1416 result.config.initial_config.host_name = strdup(argv[option_counter++]);
1417 }
1824 1418
1825 if (critical_thresholds && thlds->critical->end > (double)socket_timeout) 1419 if (result.config.initial_config.server_address == NULL) {
1826 socket_timeout = (int)thlds->critical->end + 1; 1420 if (result.config.initial_config.host_name == NULL) {
1827 if (verbose >= 2) 1421 usage4(_("You must specify a server address or host name"));
1828 printf("* Socket timeout set to %ld seconds\n", socket_timeout); 1422 } else {
1423 result.config.initial_config.server_address =
1424 strdup(result.config.initial_config.host_name);
1425 }
1426 }
1829 1427
1830 if (http_method == NULL) 1428 if (result.config.initial_config.http_method == NULL) {
1831 http_method = strdup("GET"); 1429 result.config.initial_config.http_method = strdup("GET");
1430 }
1832 1431
1833 if (client_cert && !client_privkey) 1432 if (result.config.curl_config.client_cert && !result.config.curl_config.client_privkey) {
1834 usage4(_("If you use a client certificate you must also specify a private key file")); 1433 usage4(_("If you use a client certificate you must also specify a private key file"));
1835
1836 if (virtual_port == 0)
1837 virtual_port = server_port;
1838 else {
1839 if ((use_ssl && server_port == HTTPS_PORT) || (!use_ssl && server_port == HTTP_PORT))
1840 if (!specify_port)
1841 server_port = virtual_port;
1842 } 1434 }
1843 1435
1844 return true; 1436 if (result.config.initial_config.virtualPort == 0) {
1845} 1437 result.config.initial_config.virtualPort = result.config.initial_config.serverPort;
1846 1438 } else {
1847char *perfd_time(double elapsed_time) { 1439 if ((result.config.initial_config.use_ssl &&
1848 return fperfdata("time", elapsed_time, "s", thlds->warning ? true : false, thlds->warning ? thlds->warning->end : 0, 1440 result.config.initial_config.serverPort == HTTPS_PORT) ||
1849 thlds->critical ? true : false, thlds->critical ? thlds->critical->end : 0, true, 0, true, socket_timeout); 1441 (!result.config.initial_config.use_ssl &&
1850} 1442 result.config.initial_config.serverPort == HTTP_PORT)) {
1851 1443 if (!specify_port) {
1852char *perfd_time_connect(double elapsed_time_connect) { 1444 result.config.initial_config.serverPort = result.config.initial_config.virtualPort;
1853 return fperfdata("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, socket_timeout); 1445 }
1854} 1446 }
1855 1447 }
1856char *perfd_time_ssl(double elapsed_time_ssl) {
1857 return fperfdata("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1858}
1859
1860char *perfd_time_headers(double elapsed_time_headers) {
1861 return fperfdata("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1862}
1863
1864char *perfd_time_firstbyte(double elapsed_time_firstbyte) {
1865 return fperfdata("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1866}
1867
1868char *perfd_time_transfer(double elapsed_time_transfer) {
1869 return fperfdata("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1870}
1871 1448
1872char *perfd_size(int page_len) { 1449 return result;
1873 return perfdata("size", page_len, "B", (min_page_len > 0 ? true : false), min_page_len, (min_page_len > 0 ? true : false), 0, true, 0,
1874 false, 0);
1875} 1450}
1876 1451
1877void print_help(void) { 1452void print_help(void) {
@@ -1885,7 +1460,8 @@ void print_help(void) {
1885 printf("%s\n", _("strings and regular expressions, check connection times, and report on")); 1460 printf("%s\n", _("strings and regular expressions, check connection times, and report on"));
1886 printf("%s\n", _("certificate expiration times.")); 1461 printf("%s\n", _("certificate expiration times."));
1887 printf("\n"); 1462 printf("\n");
1888 printf("%s\n", _("It makes use of libcurl to do so. It tries to be as compatible to check_http")); 1463 printf("%s\n",
1464 _("It makes use of libcurl to do so. It tries to be as compatible to check_http"));
1889 printf("%s\n", _("as possible.")); 1465 printf("%s\n", _("as possible."));
1890 1466
1891 printf("\n\n"); 1467 printf("\n\n");
@@ -1903,7 +1479,8 @@ void print_help(void) {
1903 printf(" %s\n", _("Host name argument for servers using host headers (virtual host)")); 1479 printf(" %s\n", _("Host name argument for servers using host headers (virtual host)"));
1904 printf(" %s\n", _("Append a port to include it in the header (eg: example.com:5000)")); 1480 printf(" %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1905 printf(" %s\n", "-I, --IP-address=ADDRESS"); 1481 printf(" %s\n", "-I, --IP-address=ADDRESS");
1906 printf(" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup).")); 1482 printf(" %s\n",
1483 _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1907 printf(" %s\n", "-p, --port=INTEGER"); 1484 printf(" %s\n", "-p, --port=INTEGER");
1908 printf(" %s", _("Port number (default: ")); 1485 printf(" %s", _("Port number (default: "));
1909 printf("%d)\n", HTTP_PORT); 1486 printf("%d)\n", HTTP_PORT);
@@ -1912,27 +1489,36 @@ void print_help(void) {
1912 1489
1913#ifdef LIBCURL_FEATURE_SSL 1490#ifdef LIBCURL_FEATURE_SSL
1914 printf(" %s\n", "-S, --ssl=VERSION[+]"); 1491 printf(" %s\n", "-S, --ssl=VERSION[+]");
1915 printf(" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents")); 1492 printf(" %s\n",
1493 _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents"));
1916 printf(" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,")); 1494 printf(" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,"));
1917 printf(" %s\n", _("1.2 = TLSv1.2, 1.3 = TLSv1.3). With a '+' suffix, newer versions are also accepted.")); 1495 printf(" %s\n", _("1.2 = TLSv1.2, 1.3 = TLSv1.3). With a '+' suffix, newer versions are "
1918 printf(" %s\n", _("Note: SSLv2, SSLv3, TLSv1.0 and TLSv1.1 are deprecated and are usually disabled in libcurl")); 1496 "also accepted."));
1497 printf(" %s\n", _("Note: SSLv2, SSLv3, TLSv1.0 and TLSv1.1 are deprecated and are usually "
1498 "disabled in libcurl"));
1919 printf(" %s\n", "--sni"); 1499 printf(" %s\n", "--sni");
1920 printf(" %s\n", _("Enable SSL/TLS hostname extension support (SNI)")); 1500 printf(" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
1921# if LIBCURL_VERSION_NUM >= 0x071801 1501# if LIBCURL_VERSION_NUM >= 0x071801
1922 printf(" %s\n", _("Note: --sni is the default in libcurl as SSLv2 and SSLV3 are deprecated and")); 1502 printf(" %s\n",
1503 _("Note: --sni is the default in libcurl as SSLv2 and SSLV3 are deprecated and"));
1923 printf(" %s\n", _(" SNI only really works since TLSv1.0")); 1504 printf(" %s\n", _(" SNI only really works since TLSv1.0"));
1924# else 1505# else
1925 printf(" %s\n", _("Note: SNI is not supported in libcurl before 7.18.1")); 1506 printf(" %s\n", _("Note: SNI is not supported in libcurl before 7.18.1"));
1926# endif 1507# endif
1927 printf(" %s\n", "-C, --certificate=INTEGER[,INTEGER]"); 1508 printf(" %s\n", "-C, --certificate=INTEGER[,INTEGER]");
1928 printf(" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443.")); 1509 printf(" %s\n",
1929 printf(" %s\n", _("A STATE_WARNING is returned if the certificate has a validity less than the")); 1510 _("Minimum number of days a certificate has to be valid. Port defaults to 443."));
1930 printf(" %s\n", _("first agument's value. If there is a second argument and the certificate's")); 1511 printf(" %s\n",
1512 _("A STATE_WARNING is returned if the certificate has a validity less than the"));
1513 printf(" %s\n",
1514 _("first agument's value. If there is a second argument and the certificate's"));
1931 printf(" %s\n", _("validity is less than its value, a STATE_CRITICAL is returned.")); 1515 printf(" %s\n", _("validity is less than its value, a STATE_CRITICAL is returned."));
1932 printf(" %s\n", _("(When this option is used the URL is not checked by default. You can use")); 1516 printf(" %s\n",
1517 _("(When this option is used the URL is not checked by default. You can use"));
1933 printf(" %s\n", _(" --continue-after-certificate to override this behavior)")); 1518 printf(" %s\n", _(" --continue-after-certificate to override this behavior)"));
1934 printf(" %s\n", "--continue-after-certificate"); 1519 printf(" %s\n", "--continue-after-certificate");
1935 printf(" %s\n", _("Allows the HTTP check to continue after performing the certificate check.")); 1520 printf(" %s\n",
1521 _("Allows the HTTP check to continue after performing the certificate check."));
1936 printf(" %s\n", _("Does nothing unless -C is used.")); 1522 printf(" %s\n", _("Does nothing unless -C is used."));
1937 printf(" %s\n", "-J, --client-cert=FILE"); 1523 printf(" %s\n", "-J, --client-cert=FILE");
1938 printf(" %s\n", _("Name of file that contains the client certificate (PEM format)")); 1524 printf(" %s\n", _("Name of file that contains the client certificate (PEM format)"));
@@ -1950,7 +1536,8 @@ void print_help(void) {
1950 printf(" %s\n", _("Comma-delimited list of strings, at least one of them is expected in")); 1536 printf(" %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1951 printf(" %s", _("the first (status) line of the server response (default: ")); 1537 printf(" %s", _("the first (status) line of the server response (default: "));
1952 printf("%s)\n", HTTP_EXPECT); 1538 printf("%s)\n", HTTP_EXPECT);
1953 printf(" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)")); 1539 printf(" %s\n",
1540 _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1954 printf(" %s\n", "-d, --header-string=STRING"); 1541 printf(" %s\n", "-d, --header-string=STRING");
1955 printf(" %s\n", _("String to expect in the response headers")); 1542 printf(" %s\n", _("String to expect in the response headers"));
1956 printf(" %s\n", "-s, --string=STRING"); 1543 printf(" %s\n", "-s, --string=STRING");
@@ -1959,7 +1546,8 @@ void print_help(void) {
1959 printf(" %s\n", _("URL to GET or POST (default: /)")); 1546 printf(" %s\n", _("URL to GET or POST (default: /)"));
1960 printf(" %s\n", "-P, --post=STRING"); 1547 printf(" %s\n", "-P, --post=STRING");
1961 printf(" %s\n", _("URL decoded http POST data")); 1548 printf(" %s\n", _("URL decoded http POST data"));
1962 printf(" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT)"); 1549 printf(" %s\n",
1550 "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT)");
1963 printf(" %s\n", _("Set HTTP method.")); 1551 printf(" %s\n", _("Set HTTP method."));
1964 printf(" %s\n", "-N, --no-body"); 1552 printf(" %s\n", "-N, --no-body");
1965 printf(" %s\n", _("Don't wait for document body: stop reading after headers.")); 1553 printf(" %s\n", _("Don't wait for document body: stop reading after headers."));
@@ -1979,7 +1567,8 @@ void print_help(void) {
1979 printf(" %s\n", _("Return STATE if found, OK if not (STATE is CRITICAL, per default)")); 1567 printf(" %s\n", _("Return STATE if found, OK if not (STATE is CRITICAL, per default)"));
1980 printf(" %s\n", _("can be changed with --state--regex)")); 1568 printf(" %s\n", _("can be changed with --state--regex)"));
1981 printf(" %s\n", "--state-regex=STATE"); 1569 printf(" %s\n", "--state-regex=STATE");
1982 printf(" %s\n", _("Return STATE if regex is found, OK if not. STATE can be one of \"critical\",\"warning\"")); 1570 printf(" %s\n", _("Return STATE if regex is found, OK if not. STATE can be one of "
1571 "\"critical\",\"warning\""));
1983 printf(" %s\n", "-a, --authorization=AUTH_PAIR"); 1572 printf(" %s\n", "-a, --authorization=AUTH_PAIR");
1984 printf(" %s\n", _("Username:password on sites with basic authentication")); 1573 printf(" %s\n", _("Username:password on sites with basic authentication"));
1985 printf(" %s\n", "-b, --proxy-authorization=AUTH_PAIR"); 1574 printf(" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
@@ -1987,13 +1576,14 @@ void print_help(void) {
1987 printf(" %s\n", "-A, --useragent=STRING"); 1576 printf(" %s\n", "-A, --useragent=STRING");
1988 printf(" %s\n", _("String to be sent in http header as \"User Agent\"")); 1577 printf(" %s\n", _("String to be sent in http header as \"User Agent\""));
1989 printf(" %s\n", "-k, --header=STRING"); 1578 printf(" %s\n", "-k, --header=STRING");
1990 printf(" %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers")); 1579 printf(" %s\n", _("Any other tags to be sent in http header. Use multiple times for "
1580 "additional headers"));
1991 printf(" %s\n", "-E, --extended-perfdata"); 1581 printf(" %s\n", "-E, --extended-perfdata");
1992 printf(" %s\n", _("Print additional performance data")); 1582 printf(" %s\n", _("Print additional performance data"));
1993 printf(" %s\n", "-B, --show-body"); 1583 printf(" %s\n", "-B, --show-body");
1994 printf(" %s\n", _("Print body content below status line")); 1584 printf(" %s\n", _("Print body content below status line"));
1995 printf(" %s\n", "-L, --link"); 1585 // printf(" %s\n", "-L, --link");
1996 printf(" %s\n", _("Wrap output in HTML link (obsoleted by urlize)")); 1586 // printf(" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1997 printf(" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport|curl>"); 1587 printf(" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport|curl>");
1998 printf(" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the")); 1588 printf(" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1999 printf(" %s\n", _("specified IP address. stickyport also ensures port stays the same.")); 1589 printf(" %s\n", _("specified IP address. stickyport also ensures port stays the same."));
@@ -2003,20 +1593,25 @@ void print_help(void) {
2003 printf(" %s", _("Maximal number of redirects (default: ")); 1593 printf(" %s", _("Maximal number of redirects (default: "));
2004 printf("%d)\n", DEFAULT_MAX_REDIRS); 1594 printf("%d)\n", DEFAULT_MAX_REDIRS);
2005 printf(" %s\n", "-m, --pagesize=INTEGER<:INTEGER>"); 1595 printf(" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
2006 printf(" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)")); 1596 printf(" %s\n",
1597 _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
2007 printf("\n"); 1598 printf("\n");
2008 printf(" %s\n", "--http-version=VERSION"); 1599 printf(" %s\n", "--http-version=VERSION");
2009 printf(" %s\n", _("Connect via specific HTTP protocol.")); 1600 printf(" %s\n", _("Connect via specific HTTP protocol."));
2010 printf(" %s\n", _("1.0 = HTTP/1.0, 1.1 = HTTP/1.1, 2.0 = HTTP/2 (HTTP/2 will fail without -S)")); 1601 printf(" %s\n",
1602 _("1.0 = HTTP/1.0, 1.1 = HTTP/1.1, 2.0 = HTTP/2 (HTTP/2 will fail without -S)"));
2011 printf(" %s\n", "--enable-automatic-decompression"); 1603 printf(" %s\n", "--enable-automatic-decompression");
2012 printf(" %s\n", _("Enable automatic decompression of body (CURLOPT_ACCEPT_ENCODING).")); 1604 printf(" %s\n", _("Enable automatic decompression of body (CURLOPT_ACCEPT_ENCODING)."));
2013 printf(" %s\n", "--haproxy-protocol"); 1605 printf(" %s\n", "--haproxy-protocol");
2014 printf(" %s\n", _("Send HAProxy proxy protocol v1 header (CURLOPT_HAPROXYPROTOCOL).")); 1606 printf(" %s\n", _("Send HAProxy proxy protocol v1 header (CURLOPT_HAPROXYPROTOCOL)."));
2015 printf(" %s\n", "--cookie-jar=FILE"); 1607 printf(" %s\n", "--cookie-jar=FILE");
2016 printf(" %s\n", _("Store cookies in the cookie jar and send them out when requested.")); 1608 printf(" %s\n", _("Store cookies in the cookie jar and send them out when requested."));
2017 printf(" %s\n", _("Specify an empty string as FILE to enable curl's cookie engine without saving")); 1609 printf(" %s\n",
2018 printf(" %s\n", _("the cookies to disk. Only enabling the engine without saving to disk requires")); 1610 _("Specify an empty string as FILE to enable curl's cookie engine without saving"));
2019 printf(" %s\n", _("handling multiple requests internally to curl, so use it with --onredirect=curl")); 1611 printf(" %s\n",
1612 _("the cookies to disk. Only enabling the engine without saving to disk requires"));
1613 printf(" %s\n",
1614 _("handling multiple requests internally to curl, so use it with --onredirect=curl"));
2020 printf("\n"); 1615 printf("\n");
2021 1616
2022 printf(UT_WARN_CRIT); 1617 printf(UT_WARN_CRIT);
@@ -2025,13 +1620,18 @@ void print_help(void) {
2025 1620
2026 printf(UT_VERBOSE); 1621 printf(UT_VERBOSE);
2027 1622
1623 printf(UT_OUTPUT_FORMAT);
1624
2028 printf("\n"); 1625 printf("\n");
2029 printf("%s\n", _("Notes:")); 1626 printf("%s\n", _("Notes:"));
2030 printf(" %s\n", _("This plugin will attempt to open an HTTP connection with the host.")); 1627 printf(" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
2031 printf(" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL")); 1628 printf(" %s\n",
2032 printf(" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response")); 1629 _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1630 printf(" %s\n",
1631 _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response"));
2033 printf(" %s\n", _("messages from the host result in STATE_WARNING return values. If you are")); 1632 printf(" %s\n", _("messages from the host result in STATE_WARNING return values. If you are"));
2034 printf(" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN")); 1633 printf(" %s\n",
1634 _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
2035 printf(" %s\n", _("(fully qualified domain name) as the [host_name] argument.")); 1635 printf(" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
2036 1636
2037#ifdef LIBCURL_FEATURE_SSL 1637#ifdef LIBCURL_FEATURE_SSL
@@ -2047,38 +1647,53 @@ void print_help(void) {
2047 printf("%s\n", _("Examples:")); 1647 printf("%s\n", _("Examples:"));
2048 printf(" %s\n\n", "CHECK CONTENT: check_curl -w 5 -c 10 --ssl -H www.verisign.com"); 1648 printf(" %s\n\n", "CHECK CONTENT: check_curl -w 5 -c 10 --ssl -H www.verisign.com");
2049 printf(" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,")); 1649 printf(" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
2050 printf(" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds")); 1650 printf(" %s\n",
2051 printf(" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); 1651 _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1652 printf(" %s\n",
1653 _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
2052 printf(" %s\n", _("a STATE_CRITICAL will be returned.")); 1654 printf(" %s\n", _("a STATE_CRITICAL will be returned."));
2053 printf("\n"); 1655 printf("\n");
2054 printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 14"); 1656 printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 14");
2055 printf(" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,")); 1657 printf(" %s\n",
2056 printf(" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than")); 1658 _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
2057 printf(" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when")); 1659 printf(" %s\n",
1660 _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1661 printf(" %s\n",
1662 _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
2058 printf(" %s\n\n", _("the certificate is expired.")); 1663 printf(" %s\n\n", _("the certificate is expired."));
2059 printf("\n"); 1664 printf("\n");
2060 printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 30,14"); 1665 printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 30,14");
2061 printf(" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 30 days,")); 1666 printf(" %s\n",
2062 printf(" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than")); 1667 _("When the certificate of 'www.verisign.com' is valid for more than 30 days,"));
1668 printf(" %s\n",
1669 _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
2063 printf(" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned.")); 1670 printf(" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned."));
2064 printf(" %s\n", _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days")); 1671 printf(" %s\n",
1672 _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days"));
2065#endif 1673#endif
2066 1674
2067 printf("\n %s\n", "CHECK WEBSERVER CONTENT VIA PROXY:"); 1675 printf("\n %s\n", "CHECK WEBSERVER CONTENT VIA PROXY:");
2068 printf(" %s\n", _("It is recommended to use an environment proxy like:")); 1676 printf(" %s\n", _("It is recommended to use an environment proxy like:"));
2069 printf(" %s\n", _("http_proxy=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org")); 1677 printf(" %s\n",
1678 _("http_proxy=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org"));
2070 printf(" %s\n", _("legacy proxy requests in check_http style still work:")); 1679 printf(" %s\n", _("legacy proxy requests in check_http style still work:"));
2071 printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u http://www.monitoring-plugins.org/ -H www.monitoring-plugins.org")); 1680 printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u http://www.monitoring-plugins.org/ "
1681 "-H www.monitoring-plugins.org"));
2072 1682
2073#ifdef LIBCURL_FEATURE_SSL 1683#ifdef LIBCURL_FEATURE_SSL
2074 printf("\n %s\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: "); 1684 printf("\n %s\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: ");
2075 printf(" %s\n", _("It is recommended to use an environment proxy like:")); 1685 printf(" %s\n", _("It is recommended to use an environment proxy like:"));
2076 printf(" %s\n", _("https_proxy=http://192.168.100.35:3128 ./check_curl -H www.verisign.com -S")); 1686 printf(" %s\n",
1687 _("https_proxy=http://192.168.100.35:3128 ./check_curl -H www.verisign.com -S"));
2077 printf(" %s\n", _("legacy proxy requests in check_http style still work:")); 1688 printf(" %s\n", _("legacy proxy requests in check_http style still work:"));
2078 printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u https://www.verisign.com/ -S -j CONNECT -H www.verisign.com ")); 1689 printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u https://www.verisign.com/ -S -j "
2079 printf(" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> -S(sl) -j CONNECT -H <webserver>")); 1690 "CONNECT -H www.verisign.com "));
2080 printf(" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds")); 1691 printf(" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> "
2081 printf(" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); 1692 "-S(sl) -j CONNECT -H <webserver>"));
1693 printf(" %s\n",
1694 _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1695 printf(" %s\n",
1696 _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
2082 printf(" %s\n", _("a STATE_CRITICAL will be returned.")); 1697 printf(" %s\n", _("a STATE_CRITICAL will be returned."));
2083 1698
2084#endif 1699#endif
@@ -2089,10 +1704,12 @@ void print_help(void) {
2089void print_usage(void) { 1704void print_usage(void) {
2090 printf("%s\n", _("Usage:")); 1705 printf("%s\n", _("Usage:"));
2091 printf(" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n", progname); 1706 printf(" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n", progname);
2092 printf(" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate file>] [-D]\n"); 1707 printf(" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate "
1708 "file>] [-D]\n");
2093 printf(" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n"); 1709 printf(" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n");
2094 printf(" [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport|curl>]\n"); 1710 printf(" [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport|curl>]\n");
2095 printf(" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n"); 1711 printf(" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive "
1712 "regex>]\n");
2096 printf(" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n"); 1713 printf(" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
2097 printf(" [-A string] [-k string] [-S <version>] [--sni] [--haproxy-protocol]\n"); 1714 printf(" [-A string] [-k string] [-S <version>] [--sni] [--haproxy-protocol]\n");
2098 printf(" [-T <content-type>] [-j method]\n"); 1715 printf(" [-T <content-type>] [-j method]\n");
@@ -2109,435 +1726,49 @@ void print_usage(void) {
2109 1726
2110void print_curl_version(void) { printf("%s\n", curl_version()); } 1727void print_curl_version(void) { printf("%s\n", curl_version()); }
2111 1728
2112int curlhelp_initwritebuffer(curlhelp_write_curlbuf *buf) {
2113 buf->bufsize = DEFAULT_BUFFER_SIZE;
2114 buf->buflen = 0;
2115 buf->buf = (char *)malloc((size_t)buf->bufsize);
2116 if (buf->buf == NULL)
2117 return -1;
2118 return 0;
2119}
2120
2121size_t curlhelp_buffer_write_callback(void *buffer, size_t size, size_t nmemb, void *stream) {
2122 curlhelp_write_curlbuf *buf = (curlhelp_write_curlbuf *)stream;
2123
2124 while (buf->bufsize < buf->buflen + size * nmemb + 1) {
2125 buf->bufsize = buf->bufsize * 2;
2126 buf->buf = (char *)realloc(buf->buf, buf->bufsize);
2127 if (buf->buf == NULL) {
2128 fprintf(stderr, "malloc failed (%d) %s\n", errno, strerror(errno));
2129 return -1;
2130 }
2131 }
2132
2133 memcpy(buf->buf + buf->buflen, buffer, size * nmemb);
2134 buf->buflen += size * nmemb;
2135 buf->buf[buf->buflen] = '\0';
2136
2137 return (int)(size * nmemb);
2138}
2139
2140size_t curlhelp_buffer_read_callback(void *buffer, size_t size, size_t nmemb, void *stream) {
2141 curlhelp_read_curlbuf *buf = (curlhelp_read_curlbuf *)stream;
2142
2143 size_t n = min(nmemb * size, buf->buflen - buf->pos);
2144
2145 memcpy(buffer, buf->buf + buf->pos, n);
2146 buf->pos += n;
2147
2148 return (int)n;
2149}
2150
2151void curlhelp_freewritebuffer(curlhelp_write_curlbuf *buf) {
2152 free(buf->buf);
2153 buf->buf = NULL;
2154}
2155
2156int curlhelp_initreadbuffer(curlhelp_read_curlbuf *buf, const char *data, size_t datalen) {
2157 buf->buflen = datalen;
2158 buf->buf = (char *)malloc((size_t)buf->buflen);
2159 if (buf->buf == NULL)
2160 return -1;
2161 memcpy(buf->buf, data, datalen);
2162 buf->pos = 0;
2163 return 0;
2164}
2165
2166void curlhelp_freereadbuffer(curlhelp_read_curlbuf *buf) {
2167 free(buf->buf);
2168 buf->buf = NULL;
2169}
2170
2171/* TODO: where to put this, it's actually part of sstrings2 (logically)?
2172 */
2173const char *strrstr2(const char *haystack, const char *needle) {
2174 int counter;
2175 size_t len;
2176 const char *prev_pos;
2177 const char *pos;
2178
2179 if (haystack == NULL || needle == NULL)
2180 return NULL;
2181
2182 if (haystack[0] == '\0' || needle[0] == '\0')
2183 return NULL;
2184
2185 counter = 0;
2186 prev_pos = NULL;
2187 pos = haystack;
2188 len = strlen(needle);
2189 for (;;) {
2190 pos = strstr(pos, needle);
2191 if (pos == NULL) {
2192 if (counter == 0)
2193 return NULL;
2194 return prev_pos;
2195 }
2196 counter++;
2197 prev_pos = pos;
2198 pos += len;
2199 if (*pos == '\0')
2200 return prev_pos;
2201 }
2202}
2203
2204int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) {
2205 char *first_line_end;
2206 char *p;
2207 size_t first_line_len;
2208 char *pp;
2209 const char *start;
2210 char *first_line_buf;
2211
2212 /* find last start of a new header */
2213 start = strrstr2(buf, "\r\nHTTP/");
2214 if (start != NULL) {
2215 start += 2;
2216 buf = start;
2217 }
2218
2219 first_line_end = strstr(buf, "\r\n");
2220 if (first_line_end == NULL)
2221 return -1;
2222
2223 first_line_len = (size_t)(first_line_end - buf);
2224 status_line->first_line = (char *)malloc(first_line_len + 1);
2225 if (status_line->first_line == NULL)
2226 return -1;
2227 memcpy(status_line->first_line, buf, first_line_len);
2228 status_line->first_line[first_line_len] = '\0';
2229 first_line_buf = strdup(status_line->first_line);
2230
2231 /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */
2232
2233 p = strtok(first_line_buf, "/");
2234 if (p == NULL) {
2235 free(first_line_buf);
2236 return -1;
2237 }
2238 if (strcmp(p, "HTTP") != 0) {
2239 free(first_line_buf);
2240 return -1;
2241 }
2242
2243 p = strtok(NULL, " ");
2244 if (p == NULL) {
2245 free(first_line_buf);
2246 return -1;
2247 }
2248 if (strchr(p, '.') != NULL) {
2249
2250 /* HTTP 1.x case */
2251 strtok(p, ".");
2252 status_line->http_major = (int)strtol(p, &pp, 10);
2253 if (*pp != '\0') {
2254 free(first_line_buf);
2255 return -1;
2256 }
2257 strtok(NULL, " ");
2258 status_line->http_minor = (int)strtol(p, &pp, 10);
2259 if (*pp != '\0') {
2260 free(first_line_buf);
2261 return -1;
2262 }
2263 p += 4; /* 1.x SP */
2264 } else {
2265 /* HTTP 2 case */
2266 status_line->http_major = (int)strtol(p, &pp, 10);
2267 status_line->http_minor = 0;
2268 p += 2; /* 2 SP */
2269 }
2270
2271 /* status code: "404" or "404.1", then SP */
2272
2273 p = strtok(p, " ");
2274 if (p == NULL) {
2275 free(first_line_buf);
2276 return -1;
2277 }
2278 if (strchr(p, '.') != NULL) {
2279 char *ppp;
2280 ppp = strtok(p, ".");
2281 status_line->http_code = (int)strtol(ppp, &pp, 10);
2282 if (*pp != '\0') {
2283 free(first_line_buf);
2284 return -1;
2285 }
2286 ppp = strtok(NULL, "");
2287 status_line->http_subcode = (int)strtol(ppp, &pp, 10);
2288 if (*pp != '\0') {
2289 free(first_line_buf);
2290 return -1;
2291 }
2292 p += 6; /* 400.1 SP */
2293 } else {
2294 status_line->http_code = (int)strtol(p, &pp, 10);
2295 status_line->http_subcode = -1;
2296 if (*pp != '\0') {
2297 free(first_line_buf);
2298 return -1;
2299 }
2300 p += 4; /* 400 SP */
2301 }
2302
2303 /* Human readable message: "Not Found" CRLF */
2304
2305 p = strtok(p, "");
2306 if (p == NULL) {
2307 status_line->msg = "";
2308 return 0;
2309 }
2310 status_line->msg = status_line->first_line + (p - first_line_buf);
2311 free(first_line_buf);
2312
2313 return 0;
2314}
2315
2316void curlhelp_free_statusline(curlhelp_statusline *status_line) { free(status_line->first_line); }
2317
2318char *get_header_value(const struct phr_header *headers, const size_t nof_headers, const char *header) {
2319 for (size_t i = 0; i < nof_headers; i++) {
2320 if (headers[i].name != NULL && strncasecmp(header, headers[i].name, max(headers[i].name_len, 4)) == 0) {
2321 return strndup(headers[i].value, headers[i].value_len);
2322 }
2323 }
2324 return NULL;
2325}
2326
2327int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[DEFAULT_BUFFER_SIZE]) {
2328 char *server_date = NULL;
2329 char *document_date = NULL;
2330 int date_result = STATE_OK;
2331 curlhelp_statusline status_line;
2332 struct phr_header headers[255];
2333 size_t nof_headers = 255;
2334 size_t msglen;
2335
2336 int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor,
2337 &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0);
2338
2339 if (res == -1) {
2340 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n"));
2341 }
2342
2343 server_date = get_header_value(headers, nof_headers, "date");
2344 document_date = get_header_value(headers, nof_headers, "last-modified");
2345
2346 if (!server_date || !*server_date) {
2347 char tmp[DEFAULT_BUFFER_SIZE];
2348
2349 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date unknown, "), *msg);
2350 strcpy(*msg, tmp);
2351
2352 date_result = max_state_alt(STATE_UNKNOWN, date_result);
2353
2354 } else if (!document_date || !*document_date) {
2355 char tmp[DEFAULT_BUFFER_SIZE];
2356
2357 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument modification date unknown, "), *msg);
2358 strcpy(*msg, tmp);
2359
2360 date_result = max_state_alt(STATE_CRITICAL, date_result);
2361
2362 } else {
2363 time_t srv_data = curl_getdate(server_date, NULL);
2364 time_t doc_data = curl_getdate(document_date, NULL);
2365 if (verbose >= 2)
2366 printf("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data, document_date, (int)doc_data);
2367 if (srv_data <= 0) {
2368 char tmp[DEFAULT_BUFFER_SIZE];
2369
2370 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date \"%100s\" unparsable, "), *msg, server_date);
2371 strcpy(*msg, tmp);
2372
2373 date_result = max_state_alt(STATE_CRITICAL, date_result);
2374 } else if (doc_data <= 0) {
2375 char tmp[DEFAULT_BUFFER_SIZE];
2376
2377 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date);
2378 strcpy(*msg, tmp);
2379
2380 date_result = max_state_alt(STATE_CRITICAL, date_result);
2381 } else if (doc_data > srv_data + 30) {
2382 char tmp[DEFAULT_BUFFER_SIZE];
2383
2384 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data);
2385 strcpy(*msg, tmp);
2386
2387 date_result = max_state_alt(STATE_CRITICAL, date_result);
2388 } else if (doc_data < srv_data - maximum_age) {
2389 int n = (srv_data - doc_data);
2390 if (n > (60 * 60 * 24 * 2)) {
2391 char tmp[DEFAULT_BUFFER_SIZE];
2392
2393 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), *msg, ((float)n) / (60 * 60 * 24));
2394 strcpy(*msg, tmp);
2395
2396 date_result = max_state_alt(STATE_CRITICAL, date_result);
2397 } else {
2398 char tmp[DEFAULT_BUFFER_SIZE];
2399
2400 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60);
2401 strcpy(*msg, tmp);
2402
2403 date_result = max_state_alt(STATE_CRITICAL, date_result);
2404 }
2405 }
2406 }
2407
2408 if (server_date)
2409 free(server_date);
2410 if (document_date)
2411 free(document_date);
2412
2413 return date_result;
2414}
2415
2416int get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_write_curlbuf *body_buf) {
2417 size_t content_length = 0;
2418 struct phr_header headers[255];
2419 size_t nof_headers = 255;
2420 size_t msglen;
2421 char *content_length_s = NULL;
2422 curlhelp_statusline status_line;
2423
2424 int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor,
2425 &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0);
2426
2427 if (res == -1) {
2428 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n"));
2429 }
2430
2431 content_length_s = get_header_value(headers, nof_headers, "content-length");
2432 if (!content_length_s) {
2433 return header_buf->buflen + body_buf->buflen;
2434 }
2435 content_length_s += strspn(content_length_s, " \t");
2436 content_length = atoi(content_length_s);
2437 if (content_length != body_buf->buflen) {
2438 /* TODO: should we warn if the actual and the reported body length don't match? */
2439 }
2440
2441 if (content_length_s)
2442 free(content_length_s);
2443
2444 return header_buf->buflen + body_buf->buflen;
2445}
2446
2447/* TODO: is there a better way in libcurl to check for the SSL library? */
2448curlhelp_ssl_library curlhelp_get_ssl_library(void) {
2449 curl_version_info_data *version_data;
2450 char *ssl_version;
2451 char *library;
2452 curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN;
2453
2454 version_data = curl_version_info(CURLVERSION_NOW);
2455 if (version_data == NULL)
2456 return CURLHELP_SSL_LIBRARY_UNKNOWN;
2457
2458 ssl_version = strdup(version_data->ssl_version);
2459 if (ssl_version == NULL)
2460 return CURLHELP_SSL_LIBRARY_UNKNOWN;
2461
2462 library = strtok(ssl_version, "/");
2463 if (library == NULL)
2464 return CURLHELP_SSL_LIBRARY_UNKNOWN;
2465
2466 if (strcmp(library, "OpenSSL") == 0)
2467 ssl_library = CURLHELP_SSL_LIBRARY_OPENSSL;
2468 else if (strcmp(library, "LibreSSL") == 0)
2469 ssl_library = CURLHELP_SSL_LIBRARY_LIBRESSL;
2470 else if (strcmp(library, "GnuTLS") == 0)
2471 ssl_library = CURLHELP_SSL_LIBRARY_GNUTLS;
2472 else if (strcmp(library, "NSS") == 0)
2473 ssl_library = CURLHELP_SSL_LIBRARY_NSS;
2474
2475 if (verbose >= 2)
2476 printf("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library, ssl_library);
2477
2478 free(ssl_version);
2479
2480 return ssl_library;
2481}
2482
2483const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library ssl_library) {
2484 switch (ssl_library) {
2485 case CURLHELP_SSL_LIBRARY_OPENSSL:
2486 return "OpenSSL";
2487 case CURLHELP_SSL_LIBRARY_LIBRESSL:
2488 return "LibreSSL";
2489 case CURLHELP_SSL_LIBRARY_GNUTLS:
2490 return "GnuTLS";
2491 case CURLHELP_SSL_LIBRARY_NSS:
2492 return "NSS";
2493 case CURLHELP_SSL_LIBRARY_UNKNOWN:
2494 default:
2495 return "unknown";
2496 }
2497}
2498
2499#ifdef LIBCURL_FEATURE_SSL 1729#ifdef LIBCURL_FEATURE_SSL
2500# ifndef USE_OPENSSL 1730# ifndef USE_OPENSSL
2501time_t parse_cert_date(const char *s) { 1731time_t parse_cert_date(const char *s) {
2502 struct tm tm; 1732 if (!s) {
2503 time_t date;
2504 char *res;
2505
2506 if (!s)
2507 return -1; 1733 return -1;
1734 }
2508 1735
2509 /* Jan 17 14:25:12 2020 GMT */ 1736 /* Jan 17 14:25:12 2020 GMT */
2510 res = strptime(s, "%Y-%m-%d %H:%M:%S GMT", &tm); 1737 struct tm tm;
1738 char *res = strptime(s, "%Y-%m-%d %H:%M:%S GMT", &tm);
2511 /* Sep 11 12:00:00 2020 GMT */ 1739 /* Sep 11 12:00:00 2020 GMT */
2512 if (res == NULL) 1740 if (res == NULL) {
2513 strptime(s, "%Y %m %d %H:%M:%S GMT", &tm); 1741 strptime(s, "%Y %m %d %H:%M:%S GMT", &tm);
2514 date = mktime(&tm); 1742 }
1743 time_t date = mktime(&tm);
2515 1744
2516 return date; 1745 return date;
2517} 1746}
1747# endif /* USE_OPENSSL */
1748#endif /* LIBCURL_FEATURE_SSL */
2518 1749
1750#ifdef LIBCURL_FEATURE_SSL
1751# ifndef USE_OPENSSL
2519/* TODO: this needs cleanup in the sslutils.c, maybe we the #else case to 1752/* TODO: this needs cleanup in the sslutils.c, maybe we the #else case to
2520 * OpenSSL could be this function 1753 * OpenSSL could be this function
2521 */ 1754 */
2522int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_warn, int days_till_exp_crit) { 1755int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_warn,
2523 int i; 1756 int days_till_exp_crit) {
2524 struct curl_slist *slist;
2525 int cname_found = 0;
2526 char *start_date_str = NULL;
2527 char *end_date_str = NULL;
2528 time_t start_date;
2529 time_t end_date;
2530 char *tz;
2531 float time_left;
2532 int days_left;
2533 int time_remaining;
2534 char timestamp[50] = "";
2535 int status = STATE_UNKNOWN;
2536 1757
2537 if (verbose >= 2) 1758 if (verbose >= 2) {
2538 printf("**** REQUEST CERTIFICATES ****\n"); 1759 printf("**** REQUEST CERTIFICATES ****\n");
1760 }
1761
1762 char *start_date_str = NULL;
1763 char *end_date_str = NULL;
1764 bool have_first_cert = false;
1765 bool cname_found = false;
1766 for (int i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) {
1767 if (have_first_cert) {
1768 break;
1769 }
2539 1770
2540 for (i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) { 1771 struct curl_slist *slist;
2541 for (slist = cert_ptr->to_certinfo->certinfo[i]; slist; slist = slist->next) { 1772 for (slist = cert_ptr->to_certinfo->certinfo[i]; slist; slist = slist->next) {
2542 /* find first common name in subject, 1773 /* find first common name in subject,
2543 * TODO: check alternative subjects for 1774 * TODO: check alternative subjects for
@@ -2553,7 +1784,7 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_
2553 } 1784 }
2554 if (p != NULL) { 1785 if (p != NULL) {
2555 if (strncmp(host_name, p + d, strlen(host_name)) == 0) { 1786 if (strncmp(host_name, p + d, strlen(host_name)) == 0) {
2556 cname_found = 1; 1787 cname_found = true;
2557 } 1788 }
2558 } 1789 }
2559 } else if (strncasecmp(slist->data, "Start Date:", 11) == 0) { 1790 } else if (strncasecmp(slist->data, "Start Date:", 11) == 0) {
@@ -2561,78 +1792,93 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_
2561 } else if (strncasecmp(slist->data, "Expire Date:", 12) == 0) { 1792 } else if (strncasecmp(slist->data, "Expire Date:", 12) == 0) {
2562 end_date_str = &slist->data[12]; 1793 end_date_str = &slist->data[12];
2563 } else if (strncasecmp(slist->data, "Cert:", 5) == 0) { 1794 } else if (strncasecmp(slist->data, "Cert:", 5) == 0) {
2564 goto HAVE_FIRST_CERT; 1795 have_first_cert = true;
1796 break;
2565 } 1797 }
2566 if (verbose >= 2) 1798 if (verbose >= 2) {
2567 printf("%d ** %s\n", i, slist->data); 1799 printf("%d ** %s\n", i, slist->data);
1800 }
2568 } 1801 }
2569 } 1802 }
2570HAVE_FIRST_CERT:
2571 1803
2572 if (verbose >= 2) 1804 if (verbose >= 2) {
2573 printf("**** REQUEST CERTIFICATES ****\n"); 1805 printf("**** REQUEST CERTIFICATES ****\n");
1806 }
2574 1807
2575 if (!cname_found) { 1808 if (!cname_found) {
2576 printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject.")); 1809 printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject."));
2577 return STATE_CRITICAL; 1810 return STATE_CRITICAL;
2578 } 1811 }
2579 1812
2580 start_date = parse_cert_date(start_date_str); 1813 time_t start_date = parse_cert_date(start_date_str);
2581 if (start_date <= 0) { 1814 if (start_date <= 0) {
2582 snprintf(msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Start Date' in certificate: '%s'"), start_date_str); 1815 snprintf(msg, DEFAULT_BUFFER_SIZE,
1816 _("WARNING - Unparsable 'Start Date' in certificate: '%s'"), start_date_str);
2583 puts(msg); 1817 puts(msg);
2584 return STATE_WARNING; 1818 return STATE_WARNING;
2585 } 1819 }
2586 1820
2587 end_date = parse_cert_date(end_date_str); 1821 time_t end_date = parse_cert_date(end_date_str);
2588 if (end_date <= 0) { 1822 if (end_date <= 0) {
2589 snprintf(msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Expire Date' in certificate: '%s'"), start_date_str); 1823 snprintf(msg, DEFAULT_BUFFER_SIZE,
1824 _("WARNING - Unparsable 'Expire Date' in certificate: '%s'"), start_date_str);
2590 puts(msg); 1825 puts(msg);
2591 return STATE_WARNING; 1826 return STATE_WARNING;
2592 } 1827 }
2593 1828
2594 time_left = difftime(end_date, time(NULL)); 1829 float time_left = difftime(end_date, time(NULL));
2595 days_left = time_left / 86400; 1830 int days_left = time_left / 86400;
2596 tz = getenv("TZ"); 1831 char *tz = getenv("TZ");
2597 setenv("TZ", "GMT", 1); 1832 setenv("TZ", "GMT", 1);
2598 tzset(); 1833 tzset();
1834
1835 char timestamp[50] = "";
2599 strftime(timestamp, 50, "%c %z", localtime(&end_date)); 1836 strftime(timestamp, 50, "%c %z", localtime(&end_date));
2600 if (tz) 1837 if (tz) {
2601 setenv("TZ", tz, 1); 1838 setenv("TZ", tz, 1);
2602 else 1839 } else {
2603 unsetenv("TZ"); 1840 unsetenv("TZ");
1841 }
2604 tzset(); 1842 tzset();
2605 1843
1844 mp_state_enum status = STATE_UNKNOWN;
1845 int time_remaining;
2606 if (days_left > 0 && days_left <= days_till_exp_warn) { 1846 if (days_left > 0 && days_left <= days_till_exp_warn) {
2607 printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", 1847 printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"),
2608 host_name, days_left, timestamp); 1848 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, days_left,
2609 if (days_left > days_till_exp_crit) 1849 timestamp);
1850 if (days_left > days_till_exp_crit) {
2610 status = STATE_WARNING; 1851 status = STATE_WARNING;
2611 else 1852 } else {
2612 status = STATE_CRITICAL; 1853 status = STATE_CRITICAL;
1854 }
2613 } else if (days_left == 0 && time_left > 0) { 1855 } else if (days_left == 0 && time_left > 0) {
2614 if (time_left >= 3600) 1856 if (time_left >= 3600) {
2615 time_remaining = (int)time_left / 3600; 1857 time_remaining = (int)time_left / 3600;
2616 else 1858 } else {
2617 time_remaining = (int)time_left / 60; 1859 time_remaining = (int)time_left / 60;
1860 }
2618 1861
2619 printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, 1862 printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"),
2620 time_remaining, time_left >= 3600 ? "hours" : "minutes", timestamp); 1863 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, time_remaining,
1864 time_left >= 3600 ? "hours" : "minutes", timestamp);
2621 1865
2622 if (days_left > days_till_exp_crit) 1866 if (days_left > days_till_exp_crit) {
2623 status = STATE_WARNING; 1867 status = STATE_WARNING;
2624 else 1868 } else {
2625 status = STATE_CRITICAL; 1869 status = STATE_CRITICAL;
1870 }
2626 } else if (time_left < 0) { 1871 } else if (time_left < 0) {
2627 printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), host_name, timestamp); 1872 printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), host_name, timestamp);
2628 status = STATE_CRITICAL; 1873 status = STATE_CRITICAL;
2629 } else if (days_left == 0) { 1874 } else if (days_left == 0) {
2630 printf(_("%s - Certificate '%s' just expired (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, 1875 printf(_("%s - Certificate '%s' just expired (%s).\n"),
2631 timestamp); 1876 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, timestamp);
2632 if (days_left > days_till_exp_crit) 1877 if (days_left > days_till_exp_crit) {
2633 status = STATE_WARNING; 1878 status = STATE_WARNING;
2634 else 1879 } else {
2635 status = STATE_CRITICAL; 1880 status = STATE_CRITICAL;
1881 }
2636 } else { 1882 } else {
2637 printf(_("OK - Certificate '%s' will expire on %s.\n"), host_name, timestamp); 1883 printf(_("OK - Certificate '%s' will expire on %s.\n"), host_name, timestamp);
2638 status = STATE_OK; 1884 status = STATE_OK;