summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/Makefile.am4
-rw-r--r--plugins/check_curl.c2886
-rw-r--r--plugins/check_curl.d/check_curl_helpers.c1267
-rw-r--r--plugins/check_curl.d/check_curl_helpers.h124
-rw-r--r--plugins/check_curl.d/config.h116
-rw-r--r--plugins/netutils.h136
-rw-r--r--plugins/sslutils.c255
-rw-r--r--plugins/t/check_curl.t49
-rwxr-xr-xplugins/tests/check_curl.t89
9 files changed, 2920 insertions, 2006 deletions
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index deae938d..1a9399f0 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -66,6 +66,7 @@ EXTRA_DIST = t \
66 check_hpjd.d \ 66 check_hpjd.d \
67 check_game.d \ 67 check_game.d \
68 check_radius.d \ 68 check_radius.d \
69 check_curl.d \
69 check_disk.d \ 70 check_disk.d \
70 check_time.d \ 71 check_time.d \
71 check_users.d \ 72 check_users.d \
@@ -92,6 +93,8 @@ EXTRA_DIST = t \
92 check_ntp_time.d \ 93 check_ntp_time.d \
93 check_dig.d \ 94 check_dig.d \
94 check_cluster.d \ 95 check_cluster.d \
96 check_curl.d \
97 check_cluster.d \
95 check_ups.d \ 98 check_ups.d \
96 check_fping.d 99 check_fping.d
97 100
@@ -132,6 +135,7 @@ check_cluster_LDADD = $(BASEOBJS)
132check_curl_CFLAGS = $(AM_CFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser 135check_curl_CFLAGS = $(AM_CFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser
133check_curl_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser 136check_curl_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser
134check_curl_LDADD = $(NETLIBS) $(LIBCURLLIBS) $(SSLOBJS) $(URIPARSERLIBS) picohttpparser/libpicohttpparser.a 137check_curl_LDADD = $(NETLIBS) $(LIBCURLLIBS) $(SSLOBJS) $(URIPARSERLIBS) picohttpparser/libpicohttpparser.a
138check_curl_SOURCES = check_curl.c check_curl.d/check_curl_helpers.c
135check_dbi_LDADD = $(NETLIBS) $(DBILIBS) 139check_dbi_LDADD = $(NETLIBS) $(DBILIBS)
136check_dig_LDADD = $(NETLIBS) 140check_dig_LDADD = $(NETLIBS)
137check_disk_LDADD = $(BASEOBJS) 141check_disk_LDADD = $(BASEOBJS)
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;
diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c
new file mode 100644
index 00000000..c3c2ba55
--- /dev/null
+++ b/plugins/check_curl.d/check_curl_helpers.c
@@ -0,0 +1,1267 @@
1#include "./check_curl_helpers.h"
2#include <stdbool.h>
3#include <arpa/inet.h>
4#include <netinet/in.h>
5#include <netdb.h>
6#include <stdlib.h>
7#include "../utils.h"
8#include "check_curl.d/config.h"
9#include "output.h"
10#include "perfdata.h"
11#include "states.h"
12
13extern int verbose;
14char errbuf[MAX_INPUT_BUFFER];
15bool is_openssl_callback = false;
16bool add_sslctx_verify_fun = false;
17
18check_curl_configure_curl_wrapper
19check_curl_configure_curl(const check_curl_static_curl_config config,
20 check_curl_working_state working_state, bool check_cert,
21 bool on_redirect_dependent, int follow_method, int max_depth) {
22 check_curl_configure_curl_wrapper result = {
23 .errorcode = OK,
24 .curl_state =
25 {
26 .curl_global_initialized = false,
27 .curl_easy_initialized = false,
28 .curl = NULL,
29
30 .body_buf_initialized = false,
31 .body_buf = NULL,
32 .header_buf_initialized = false,
33 .header_buf = NULL,
34 .status_line_initialized = false,
35 .status_line = NULL,
36 .put_buf_initialized = false,
37 .put_buf = NULL,
38
39 .header_list = NULL,
40 .host = NULL,
41 },
42 };
43
44 if ((result.curl_state.status_line = calloc(1, sizeof(curlhelp_statusline))) == NULL) {
45 die(STATE_UNKNOWN, "HTTP UNKNOWN - allocation of statusline failed\n");
46 }
47
48 if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) {
49 die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n");
50 }
51 result.curl_state.curl_global_initialized = true;
52
53 if ((result.curl_state.curl = curl_easy_init()) == NULL) {
54 die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n");
55 }
56 result.curl_state.curl_easy_initialized = true;
57
58 if (verbose >= 1) {
59 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_VERBOSE, 1),
60 "CURLOPT_VERBOSE");
61 }
62
63 /* print everything on stdout like check_http would do */
64 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_STDERR, stdout),
65 "CURLOPT_STDERR");
66
67 if (config.automatic_decompression) {
68#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6)
69 handle_curl_option_return_code(
70 curl_easy_setopt(result.curl_state.curl, CURLOPT_ACCEPT_ENCODING, ""),
71 "CURLOPT_ACCEPT_ENCODING");
72#else
73 handle_curl_option_return_code(
74 curl_easy_setopt(result.curl_state.curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING");
75#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */
76 }
77
78 /* initialize buffer for body of the answer */
79 if (curlhelp_initwritebuffer(&result.curl_state.body_buf) < 0) {
80 die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n");
81 }
82 result.curl_state.body_buf_initialized = true;
83
84 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEFUNCTION,
85 curlhelp_buffer_write_callback),
86 "CURLOPT_WRITEFUNCTION");
87 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEDATA,
88 (void *)result.curl_state.body_buf),
89 "CURLOPT_WRITEDATA");
90
91 /* initialize buffer for header of the answer */
92 if (curlhelp_initwritebuffer(&result.curl_state.header_buf) < 0) {
93 die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n");
94 }
95 result.curl_state.header_buf_initialized = true;
96
97 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_HEADERFUNCTION,
98 curlhelp_buffer_write_callback),
99 "CURLOPT_HEADERFUNCTION");
100 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEHEADER,
101 (void *)result.curl_state.header_buf),
102 "CURLOPT_WRITEHEADER");
103
104 /* set the error buffer */
105 handle_curl_option_return_code(
106 curl_easy_setopt(result.curl_state.curl, CURLOPT_ERRORBUFFER, errbuf),
107 "CURLOPT_ERRORBUFFER");
108
109 /* set timeouts */
110 handle_curl_option_return_code(
111 curl_easy_setopt(result.curl_state.curl, CURLOPT_CONNECTTIMEOUT, config.socket_timeout),
112 "CURLOPT_CONNECTTIMEOUT");
113
114 handle_curl_option_return_code(
115 curl_easy_setopt(result.curl_state.curl, CURLOPT_TIMEOUT, config.socket_timeout),
116 "CURLOPT_TIMEOUT");
117
118 /* enable haproxy protocol */
119 if (config.haproxy_protocol) {
120 handle_curl_option_return_code(
121 curl_easy_setopt(result.curl_state.curl, CURLOPT_HAPROXYPROTOCOL, 1L),
122 "CURLOPT_HAPROXYPROTOCOL");
123 }
124
125 // fill dns resolve cache to make curl connect to the given server_address instead of the
126 // host_name, only required for ssl, because we use the host_name later on to make SNI happy
127 char dnscache[DEFAULT_BUFFER_SIZE];
128 char addrstr[DEFAULT_BUFFER_SIZE / 2];
129 if (working_state.use_ssl && working_state.host_name != NULL) {
130 int res;
131 if ((res = lookup_host(working_state.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2,
132 config.sin_family)) != 0) {
133 die(STATE_CRITICAL,
134 _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"),
135 working_state.server_address, res, gai_strerror(res));
136 }
137
138 snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", working_state.host_name,
139 working_state.serverPort, addrstr);
140 result.curl_state.host = curl_slist_append(NULL, dnscache);
141 curl_easy_setopt(result.curl_state.curl, CURLOPT_RESOLVE, result.curl_state.host);
142
143 if (verbose >= 1) {
144 printf("* curl CURLOPT_RESOLVE: %s\n", dnscache);
145 }
146 }
147
148 // If server_address is an IPv6 address it must be surround by square brackets
149 struct in6_addr tmp_in_addr;
150 if (inet_pton(AF_INET6, working_state.server_address, &tmp_in_addr) == 1) {
151 char *new_server_address = calloc(strlen(working_state.server_address) + 3, sizeof(char));
152 if (new_server_address == NULL) {
153 die(STATE_UNKNOWN, "HTTP UNKNOWN - Unable to allocate memory\n");
154 }
155 snprintf(new_server_address, strlen(working_state.server_address) + 3, "[%s]",
156 working_state.server_address);
157 working_state.server_address = new_server_address;
158 }
159
160 /* compose URL: use the address we want to connect to, set Host: header later */
161 char *url = fmt_url(working_state);
162
163 if (verbose >= 1) {
164 printf("* curl CURLOPT_URL: %s\n", url);
165 }
166 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, url),
167 "CURLOPT_URL");
168
169 free(url);
170
171 /* extract proxy information for legacy proxy https requests */
172 if (!strcmp(working_state.http_method, "CONNECT") ||
173 strstr(working_state.server_url, "http") == working_state.server_url) {
174 handle_curl_option_return_code(
175 curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXY, working_state.server_address),
176 "CURLOPT_PROXY");
177 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXYPORT,
178 (long)working_state.serverPort),
179 "CURLOPT_PROXYPORT");
180 if (verbose >= 2) {
181 printf("* curl CURLOPT_PROXY: %s:%d\n", working_state.server_address,
182 working_state.serverPort);
183 }
184 working_state.http_method = "GET";
185 handle_curl_option_return_code(
186 curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, working_state.server_url),
187 "CURLOPT_URL");
188 }
189
190 /* disable body for HEAD request */
191 if (working_state.http_method && !strcmp(working_state.http_method, "HEAD")) {
192 working_state.no_body = true;
193 }
194
195 /* set HTTP protocol version */
196 handle_curl_option_return_code(
197 curl_easy_setopt(result.curl_state.curl, CURLOPT_HTTP_VERSION, config.curl_http_version),
198 "CURLOPT_HTTP_VERSION");
199
200 /* set HTTP method */
201 if (working_state.http_method) {
202 if (!strcmp(working_state.http_method, "POST")) {
203 handle_curl_option_return_code(
204 curl_easy_setopt(result.curl_state.curl, CURLOPT_POST, 1), "CURLOPT_POST");
205 } else if (!strcmp(working_state.http_method, "PUT")) {
206 handle_curl_option_return_code(
207 curl_easy_setopt(result.curl_state.curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD");
208 } else {
209 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl,
210 CURLOPT_CUSTOMREQUEST,
211 working_state.http_method),
212 "CURLOPT_CUSTOMREQUEST");
213 }
214 }
215
216 char *force_host_header = NULL;
217 /* check if Host header is explicitly set in options */
218 if (config.http_opt_headers_count) {
219 for (size_t i = 0; i < config.http_opt_headers_count; i++) {
220 if (strncmp(config.http_opt_headers[i], "Host:", 5) == 0) {
221 force_host_header = config.http_opt_headers[i];
222 }
223 }
224 }
225
226 /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in
227 * anyway */
228 char http_header[DEFAULT_BUFFER_SIZE];
229 if (working_state.host_name != NULL && force_host_header == NULL) {
230 if ((working_state.virtualPort != HTTP_PORT && !working_state.use_ssl) ||
231 (working_state.virtualPort != HTTPS_PORT && working_state.use_ssl)) {
232 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", working_state.host_name,
233 working_state.virtualPort);
234 } else {
235 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", working_state.host_name);
236 }
237 result.curl_state.header_list =
238 curl_slist_append(result.curl_state.header_list, http_header);
239 }
240
241 /* always close connection, be nice to servers */
242 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Connection: close");
243 result.curl_state.header_list = curl_slist_append(result.curl_state.header_list, http_header);
244
245 /* attach additional headers supplied by the user */
246 /* optionally send any other header tag */
247 if (config.http_opt_headers_count) {
248 for (size_t i = 0; i < config.http_opt_headers_count; i++) {
249 result.curl_state.header_list =
250 curl_slist_append(result.curl_state.header_list, config.http_opt_headers[i]);
251 }
252 }
253
254 /* set HTTP headers */
255 handle_curl_option_return_code(
256 curl_easy_setopt(result.curl_state.curl, CURLOPT_HTTPHEADER, result.curl_state.header_list),
257 "CURLOPT_HTTPHEADER");
258
259#ifdef LIBCURL_FEATURE_SSL
260 /* set SSL version, warn about insecure or unsupported versions */
261 if (working_state.use_ssl) {
262 handle_curl_option_return_code(
263 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLVERSION, config.ssl_version),
264 "CURLOPT_SSLVERSION");
265 }
266
267 /* client certificate and key to present to server (SSL) */
268 if (config.client_cert) {
269 handle_curl_option_return_code(
270 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLCERT, config.client_cert),
271 "CURLOPT_SSLCERT");
272 }
273
274 if (config.client_privkey) {
275 handle_curl_option_return_code(
276 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLKEY, config.client_privkey),
277 "CURLOPT_SSLKEY");
278 }
279
280 if (config.ca_cert) {
281 handle_curl_option_return_code(
282 curl_easy_setopt(result.curl_state.curl, CURLOPT_CAINFO, config.ca_cert),
283 "CURLOPT_CAINFO");
284 }
285
286 if (config.ca_cert || config.verify_peer_and_host) {
287 /* per default if we have a CA verify both the peer and the
288 * hostname in the certificate, can be switched off later */
289 handle_curl_option_return_code(
290 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 1),
291 "CURLOPT_SSL_VERIFYPEER");
292 handle_curl_option_return_code(
293 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 2),
294 "CURLOPT_SSL_VERIFYHOST");
295 } else {
296 /* backward-compatible behaviour, be tolerant in checks
297 * TODO: depending on more options have aspects we want
298 * to be less tolerant about ssl verfications
299 */
300 handle_curl_option_return_code(
301 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 0),
302 "CURLOPT_SSL_VERIFYPEER");
303 handle_curl_option_return_code(
304 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 0),
305 "CURLOPT_SSL_VERIFYHOST");
306 }
307
308 /* detect SSL library used by libcurl */
309 curlhelp_ssl_library ssl_library = curlhelp_get_ssl_library();
310
311 /* try hard to get a stack of certificates to verify against */
312 if (check_cert) {
313# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1)
314 /* inform curl to report back certificates */
315 switch (ssl_library) {
316 case CURLHELP_SSL_LIBRARY_OPENSSL:
317 case CURLHELP_SSL_LIBRARY_LIBRESSL:
318 /* set callback to extract certificate with OpenSSL context function (works with
319 * OpenSSL-style libraries only!) */
320# ifdef USE_OPENSSL
321 /* libcurl and monitoring plugins built with OpenSSL, good */
322 add_sslctx_verify_fun = true;
323 is_openssl_callback = true;
324# endif /* USE_OPENSSL */
325 /* libcurl is built with OpenSSL, monitoring plugins, so falling
326 * back to manually extracting certificate information */
327 handle_curl_option_return_code(
328 curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
329 break;
330
331 case CURLHELP_SSL_LIBRARY_NSS:
332# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
333 /* NSS: support for CERTINFO is implemented since 7.34.0 */
334 handle_curl_option_return_code(
335 curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
336# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
337 die(STATE_CRITICAL,
338 "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library "
339 "'%s' is too old)\n",
340 curlhelp_get_ssl_library_string(ssl_library));
341# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
342 break;
343
344 case CURLHELP_SSL_LIBRARY_GNUTLS:
345# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0)
346 /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */
347 handle_curl_option_return_code(
348 curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
349# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
350 die(STATE_CRITICAL,
351 "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library "
352 "'%s' is too old)\n",
353 curlhelp_get_ssl_library_string(ssl_library));
354# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
355 break;
356
357 case CURLHELP_SSL_LIBRARY_UNKNOWN:
358 default:
359 die(STATE_CRITICAL,
360 "HTTP CRITICAL - Cannot retrieve certificates (unknown SSL library '%s', must "
361 "implement first)\n",
362 curlhelp_get_ssl_library_string(ssl_library));
363 break;
364 }
365# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
366 /* old libcurl, our only hope is OpenSSL, otherwise we are out of luck */
367 if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL ||
368 ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL) {
369 add_sslctx_verify_fun = true;
370 } else {
371 die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no "
372 "CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl "
373 "too old and has no CURLOPT_CERTINFO)\n");
374 }
375# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
376 }
377
378# if LIBCURL_VERSION_NUM >= \
379 MAKE_LIBCURL_VERSION(7, 10, 6) /* required for CURLOPT_SSL_CTX_FUNCTION */
380 // ssl ctx function is not available with all ssl backends
381 if (curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_CTX_FUNCTION, NULL) !=
382 CURLE_UNKNOWN_OPTION) {
383 handle_curl_option_return_code(
384 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun),
385 "CURLOPT_SSL_CTX_FUNCTION");
386 }
387# endif
388#endif /* LIBCURL_FEATURE_SSL */
389
390 /* set default or user-given user agent identification */
391 handle_curl_option_return_code(
392 curl_easy_setopt(result.curl_state.curl, CURLOPT_USERAGENT, config.user_agent),
393 "CURLOPT_USERAGENT");
394
395 /* proxy-authentication */
396 if (strcmp(config.proxy_auth, "")) {
397 handle_curl_option_return_code(
398 curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXYUSERPWD, config.proxy_auth),
399 "CURLOPT_PROXYUSERPWD");
400 }
401
402 /* authentication */
403 if (strcmp(config.user_auth, "")) {
404 handle_curl_option_return_code(
405 curl_easy_setopt(result.curl_state.curl, CURLOPT_USERPWD, config.user_auth),
406 "CURLOPT_USERPWD");
407 }
408 /* TODO: parameter auth method, bitfield of following methods:
409 * CURLAUTH_BASIC (default)
410 * CURLAUTH_DIGEST
411 * CURLAUTH_DIGEST_IE
412 * CURLAUTH_NEGOTIATE
413 * CURLAUTH_NTLM
414 * CURLAUTH_NTLM_WB
415 *
416 * convenience tokens for typical sets of methods:
417 * CURLAUTH_ANYSAFE: most secure, without BASIC
418 * or CURLAUTH_ANY: most secure, even BASIC if necessary
419 *
420 * handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPAUTH,
421 * (long)CURLAUTH_DIGEST ), "CURLOPT_HTTPAUTH");
422 */
423
424 /* handle redirections */
425 if (on_redirect_dependent) {
426 if (follow_method == FOLLOW_LIBCURL) {
427 handle_curl_option_return_code(
428 curl_easy_setopt(result.curl_state.curl, CURLOPT_FOLLOWLOCATION, 1),
429 "CURLOPT_FOLLOWLOCATION");
430
431 /* default -1 is infinite, not good, could lead to zombie plugins!
432 Setting it to one bigger than maximal limit to handle errors nicely below
433 */
434 handle_curl_option_return_code(
435 curl_easy_setopt(result.curl_state.curl, CURLOPT_MAXREDIRS, max_depth + 1),
436 "CURLOPT_MAXREDIRS");
437
438 /* for now allow only http and https (we are a http(s) check plugin in the end) */
439#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 85, 0)
440 handle_curl_option_return_code(
441 curl_easy_setopt(result.curl_state.curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"),
442 "CURLOPT_REDIR_PROTOCOLS_STR");
443#elif LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4)
444 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl,
445 CURLOPT_REDIR_PROTOCOLS,
446 CURLPROTO_HTTP | CURLPROTO_HTTPS),
447 "CURLOPT_REDIRECT_PROTOCOLS");
448#endif
449
450 /* TODO: handle the following aspects of redirection, make them
451 * command line options too later:
452 CURLOPT_POSTREDIR: method switch
453 CURLINFO_REDIRECT_URL: custom redirect option
454 CURLOPT_REDIRECT_PROTOCOLS: allow people to step outside safe protocols
455 CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range
456 option here is nice like for expected page size?
457 */
458 } else {
459 /* old style redirection*/
460 }
461 }
462 /* no-body */
463 if (working_state.no_body) {
464 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_NOBODY, 1),
465 "CURLOPT_NOBODY");
466 }
467
468 /* IPv4 or IPv6 forced DNS resolution */
469 if (config.sin_family == AF_UNSPEC) {
470 handle_curl_option_return_code(
471 curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER),
472 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)");
473 } else if (config.sin_family == AF_INET) {
474 handle_curl_option_return_code(
475 curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4),
476 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)");
477 }
478#if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6)
479 else if (config.sin_family == AF_INET6) {
480 handle_curl_option_return_code(
481 curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6),
482 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)");
483 }
484#endif
485
486 /* either send http POST data (any data, not only POST)*/
487 if (!strcmp(working_state.http_method, "POST") || !strcmp(working_state.http_method, "PUT")) {
488 /* set content of payload for POST and PUT */
489 if (config.http_content_type) {
490 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s",
491 config.http_content_type);
492 result.curl_state.header_list =
493 curl_slist_append(result.curl_state.header_list, http_header);
494 }
495 /* NULL indicates "HTTP Continue" in libcurl, provide an empty string
496 * in case of no POST/PUT data */
497 if (!working_state.http_post_data) {
498 working_state.http_post_data = "";
499 }
500
501 if (!strcmp(working_state.http_method, "POST")) {
502 /* POST method, set payload with CURLOPT_POSTFIELDS */
503 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl,
504 CURLOPT_POSTFIELDS,
505 working_state.http_post_data),
506 "CURLOPT_POSTFIELDS");
507 } else if (!strcmp(working_state.http_method, "PUT")) {
508 handle_curl_option_return_code(
509 curl_easy_setopt(result.curl_state.curl, CURLOPT_READFUNCTION,
510 (curl_read_callback)curlhelp_buffer_read_callback),
511 "CURLOPT_READFUNCTION");
512 if (curlhelp_initreadbuffer(&result.curl_state.put_buf, working_state.http_post_data,
513 strlen(working_state.http_post_data)) < 0) {
514 die(STATE_UNKNOWN,
515 "HTTP CRITICAL - out of memory allocating read buffer for PUT\n");
516 }
517 result.curl_state.put_buf_initialized = true;
518 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl,
519 CURLOPT_READDATA,
520 (void *)result.curl_state.put_buf),
521 "CURLOPT_READDATA");
522 handle_curl_option_return_code(
523 curl_easy_setopt(result.curl_state.curl, CURLOPT_INFILESIZE,
524 (curl_off_t)strlen(working_state.http_post_data)),
525 "CURLOPT_INFILESIZE");
526 }
527 }
528
529 /* cookie handling */
530 if (config.cookie_jar_file != NULL) {
531 /* enable reading cookies from a file, and if the filename is an empty string, only
532 * enable the curl cookie engine */
533 handle_curl_option_return_code(
534 curl_easy_setopt(result.curl_state.curl, CURLOPT_COOKIEFILE, config.cookie_jar_file),
535 "CURLOPT_COOKIEFILE");
536 /* now enable saving cookies to a file, but only if the filename is not an empty string,
537 * since writing it would fail */
538 if (*config.cookie_jar_file) {
539 handle_curl_option_return_code(
540 curl_easy_setopt(result.curl_state.curl, CURLOPT_COOKIEJAR, config.cookie_jar_file),
541 "CURLOPT_COOKIEJAR");
542 }
543 }
544
545 result.working_state = working_state;
546
547 return result;
548}
549
550void handle_curl_option_return_code(CURLcode res, const char *option) {
551 if (res != CURLE_OK) {
552 die(STATE_CRITICAL, _("Error while setting cURL option '%s': cURL returned %d - %s"),
553 option, res, curl_easy_strerror(res));
554 }
555}
556
557char *get_header_value(const struct phr_header *headers, const size_t nof_headers,
558 const char *header) {
559 for (size_t i = 0; i < nof_headers; i++) {
560 if (headers[i].name != NULL &&
561 strncasecmp(header, headers[i].name, max(headers[i].name_len, 4)) == 0) {
562 return strndup(headers[i].value, headers[i].value_len);
563 }
564 }
565 return NULL;
566}
567
568check_curl_working_state check_curl_working_state_init() {
569 check_curl_working_state result = {
570 .server_address = NULL,
571 .server_url = DEFAULT_SERVER_URL,
572 .host_name = NULL,
573 .http_method = NULL,
574 .http_post_data = NULL,
575 .virtualPort = 0,
576 .serverPort = HTTP_PORT,
577 .use_ssl = false,
578 .no_body = false,
579 };
580 return result;
581}
582
583check_curl_config check_curl_config_init() {
584 check_curl_config tmp = {
585 .initial_config = check_curl_working_state_init(),
586
587 .curl_config =
588 {
589 .automatic_decompression = false,
590 .socket_timeout = DEFAULT_SOCKET_TIMEOUT,
591 .haproxy_protocol = false,
592 .sin_family = AF_UNSPEC,
593 .curl_http_version = CURL_HTTP_VERSION_NONE,
594 .http_opt_headers = NULL,
595 .http_opt_headers_count = 0,
596 .ssl_version = CURL_SSLVERSION_DEFAULT,
597 .client_cert = NULL,
598 .client_privkey = NULL,
599 .ca_cert = NULL,
600 .verify_peer_and_host = false,
601 .user_agent = {'\0'},
602 .proxy_auth = "",
603 .user_auth = "",
604 .http_content_type = NULL,
605 .cookie_jar_file = NULL,
606 },
607 .max_depth = DEFAULT_MAX_REDIRS,
608 .followmethod = FOLLOW_HTTP_CURL,
609 .followsticky = STICKY_NONE,
610
611 .maximum_age = -1,
612 .regexp = {},
613 .compiled_regex = {},
614 .state_regex = STATE_CRITICAL,
615 .invert_regex = false,
616 .check_cert = false,
617 .continue_after_check_cert = false,
618 .days_till_exp_warn = 0,
619 .days_till_exp_crit = 0,
620 .thlds = mp_thresholds_init(),
621 .page_length_limits = mp_range_init(),
622 .page_length_limits_is_set = false,
623 .server_expect =
624 {
625 .string = HTTP_EXPECT,
626 .is_present = false,
627 },
628 .string_expect = "",
629 .header_expect = "",
630 .on_redirect_result_state = STATE_OK,
631 .on_redirect_dependent = false,
632
633 .show_extended_perfdata = false,
634 .show_body = false,
635
636 .output_format_is_set = false,
637 };
638
639 snprintf(tmp.curl_config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)",
640 "check_curl", NP_VERSION, VERSION, curl_version());
641
642 return tmp;
643}
644
645/* TODO: is there a better way in libcurl to check for the SSL library? */
646curlhelp_ssl_library curlhelp_get_ssl_library(void) {
647 curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN;
648
649 curl_version_info_data *version_data = curl_version_info(CURLVERSION_NOW);
650 if (version_data == NULL) {
651 return CURLHELP_SSL_LIBRARY_UNKNOWN;
652 }
653
654 char *ssl_version = strdup(version_data->ssl_version);
655 if (ssl_version == NULL) {
656 return CURLHELP_SSL_LIBRARY_UNKNOWN;
657 }
658
659 char *library = strtok(ssl_version, "/");
660 if (library == NULL) {
661 return CURLHELP_SSL_LIBRARY_UNKNOWN;
662 }
663
664 if (strcmp(library, "OpenSSL") == 0) {
665 ssl_library = CURLHELP_SSL_LIBRARY_OPENSSL;
666 } else if (strcmp(library, "LibreSSL") == 0) {
667 ssl_library = CURLHELP_SSL_LIBRARY_LIBRESSL;
668 } else if (strcmp(library, "GnuTLS") == 0) {
669 ssl_library = CURLHELP_SSL_LIBRARY_GNUTLS;
670 } else if (strcmp(library, "NSS") == 0) {
671 ssl_library = CURLHELP_SSL_LIBRARY_NSS;
672 }
673
674 if (verbose >= 2) {
675 printf("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library,
676 ssl_library);
677 }
678
679 free(ssl_version);
680
681 return ssl_library;
682}
683
684const char *curlhelp_get_ssl_library_string(const curlhelp_ssl_library ssl_library) {
685 switch (ssl_library) {
686 case CURLHELP_SSL_LIBRARY_OPENSSL:
687 return "OpenSSL";
688 case CURLHELP_SSL_LIBRARY_LIBRESSL:
689 return "LibreSSL";
690 case CURLHELP_SSL_LIBRARY_GNUTLS:
691 return "GnuTLS";
692 case CURLHELP_SSL_LIBRARY_NSS:
693 return "NSS";
694 case CURLHELP_SSL_LIBRARY_UNKNOWN:
695 default:
696 return "unknown";
697 }
698}
699
700size_t get_content_length(const curlhelp_write_curlbuf *header_buf,
701 const curlhelp_write_curlbuf *body_buf) {
702 struct phr_header headers[255];
703 size_t nof_headers = 255;
704 size_t msglen;
705 curlhelp_statusline status_line;
706 int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major,
707 &status_line.http_minor, &status_line.http_code, &status_line.msg,
708 &msglen, headers, &nof_headers, 0);
709
710 if (res == -1) {
711 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n"));
712 }
713
714 char *content_length_s = get_header_value(headers, nof_headers, "content-length");
715 if (!content_length_s) {
716 return header_buf->buflen + body_buf->buflen;
717 }
718
719 content_length_s += strspn(content_length_s, " \t");
720 size_t content_length = atoi(content_length_s);
721 if (content_length != body_buf->buflen) {
722 /* TODO: should we warn if the actual and the reported body length don't match? */
723 }
724
725 if (content_length_s) {
726 free(content_length_s);
727 }
728
729 return header_buf->buflen + body_buf->buflen;
730}
731
732mp_subcheck check_document_dates(const curlhelp_write_curlbuf *header_buf, const int maximum_age) {
733 struct phr_header headers[255];
734 size_t nof_headers = 255;
735 curlhelp_statusline status_line;
736 size_t msglen;
737 int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major,
738 &status_line.http_minor, &status_line.http_code, &status_line.msg,
739 &msglen, headers, &nof_headers, 0);
740
741 if (res == -1) {
742 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n"));
743 }
744
745 char *server_date = get_header_value(headers, nof_headers, "date");
746 char *document_date = get_header_value(headers, nof_headers, "last-modified");
747
748 mp_subcheck sc_document_dates = mp_subcheck_init();
749 if (!server_date || !*server_date) {
750 xasprintf(&sc_document_dates.output, _("Server date unknown"));
751 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_UNKNOWN);
752 } else if (!document_date || !*document_date) {
753 xasprintf(&sc_document_dates.output, _("Document modification date unknown, "));
754 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
755 } else {
756 time_t srv_data = curl_getdate(server_date, NULL);
757 time_t doc_data = curl_getdate(document_date, NULL);
758
759 if (verbose >= 2) {
760 printf("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data,
761 document_date, (int)doc_data);
762 }
763
764 if (srv_data <= 0) {
765 xasprintf(&sc_document_dates.output, _("Server date \"%100s\" unparsable"),
766 server_date);
767 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
768 } else if (doc_data <= 0) {
769
770 xasprintf(&sc_document_dates.output, _("Document date \"%100s\" unparsable"),
771 document_date);
772 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
773 } else if (doc_data > srv_data + 30) {
774
775 xasprintf(&sc_document_dates.output, _("Document is %d seconds in the future"),
776 (int)doc_data - (int)srv_data);
777
778 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
779 } else if (doc_data < srv_data - maximum_age) {
780 time_t last_modified = (srv_data - doc_data);
781 if (last_modified > (60 * 60 * 24 * 2)) { // two days hardcoded?
782 xasprintf(&sc_document_dates.output, _("Last modified %.1f days ago"),
783 ((float)last_modified) / (60 * 60 * 24));
784 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
785 } else {
786 xasprintf(&sc_document_dates.output, _("Last modified %ld:%02ld:%02ld ago"),
787 last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60);
788 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
789 }
790 } else {
791 // TODO is this the OK case?
792 time_t last_modified = (srv_data - doc_data);
793 xasprintf(&sc_document_dates.output, _("Last modified %ld:%02ld:%02ld ago"),
794 last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60);
795 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_OK);
796 }
797 }
798
799 if (server_date) {
800 free(server_date);
801 }
802 if (document_date) {
803 free(document_date);
804 }
805
806 return sc_document_dates;
807}
808
809void curlhelp_free_statusline(curlhelp_statusline *status_line) { free(status_line->first_line); }
810
811int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) {
812 /* find last start of a new header */
813 const char *start = strrstr2(buf, "\r\nHTTP/");
814 if (start != NULL) {
815 start += 2;
816 buf = start;
817 }
818
819 char *first_line_end = strstr(buf, "\r\n");
820 if (first_line_end == NULL) {
821 return -1;
822 }
823
824 size_t first_line_len = (size_t)(first_line_end - buf);
825 status_line->first_line = (char *)calloc(first_line_len + 1, sizeof(char));
826 if (status_line->first_line == NULL) {
827 return -1;
828 }
829 memcpy(status_line->first_line, buf, first_line_len);
830 status_line->first_line[first_line_len] = '\0';
831 char *first_line_buf = strdup(status_line->first_line);
832
833 /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */
834 char *temp_string = strtok(first_line_buf, "/");
835 if (temp_string == NULL) {
836 free(first_line_buf);
837 return -1;
838 }
839 if (strcmp(temp_string, "HTTP") != 0) {
840 free(first_line_buf);
841 return -1;
842 }
843
844 temp_string = strtok(NULL, " ");
845 if (temp_string == NULL) {
846 free(first_line_buf);
847 return -1;
848 }
849
850 char *temp_string_2;
851 if (strchr(temp_string, '.') != NULL) {
852
853 /* HTTP 1.x case */
854 strtok(temp_string, ".");
855 status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10);
856 if (*temp_string_2 != '\0') {
857 free(first_line_buf);
858 return -1;
859 }
860 strtok(NULL, " ");
861 status_line->http_minor = (int)strtol(temp_string, &temp_string_2, 10);
862 if (*temp_string_2 != '\0') {
863 free(first_line_buf);
864 return -1;
865 }
866 temp_string += 4; /* 1.x SP */
867 } else {
868 /* HTTP 2 case */
869 status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10);
870 status_line->http_minor = 0;
871 temp_string += 2; /* 2 SP */
872 }
873
874 /* status code: "404" or "404.1", then SP */
875 temp_string = strtok(temp_string, " ");
876 if (temp_string == NULL) {
877 free(first_line_buf);
878 return -1;
879 }
880 if (strchr(temp_string, '.') != NULL) {
881 char *ppp;
882 ppp = strtok(temp_string, ".");
883 status_line->http_code = (int)strtol(ppp, &temp_string_2, 10);
884 if (*temp_string_2 != '\0') {
885 free(first_line_buf);
886 return -1;
887 }
888 ppp = strtok(NULL, "");
889 status_line->http_subcode = (int)strtol(ppp, &temp_string_2, 10);
890 if (*temp_string_2 != '\0') {
891 free(first_line_buf);
892 return -1;
893 }
894 temp_string += 6; /* 400.1 SP */
895 } else {
896 status_line->http_code = (int)strtol(temp_string, &temp_string_2, 10);
897 status_line->http_subcode = -1;
898 if (*temp_string_2 != '\0') {
899 free(first_line_buf);
900 return -1;
901 }
902 temp_string += 4; /* 400 SP */
903 }
904
905 /* Human readable message: "Not Found" CRLF */
906
907 temp_string = strtok(temp_string, "");
908 if (temp_string == NULL) {
909 status_line->msg = "";
910 return 0;
911 }
912 status_line->msg = status_line->first_line + (temp_string - first_line_buf);
913 free(first_line_buf);
914
915 return 0;
916}
917
918/* TODO: where to put this, it's actually part of sstrings2 (logically)?
919 */
920const char *strrstr2(const char *haystack, const char *needle) {
921 if (haystack == NULL || needle == NULL) {
922 return NULL;
923 }
924
925 if (haystack[0] == '\0' || needle[0] == '\0') {
926 return NULL;
927 }
928
929 int counter = 0;
930 const char *prev_pos = NULL;
931 const char *pos = haystack;
932 size_t len = strlen(needle);
933 for (;;) {
934 pos = strstr(pos, needle);
935 if (pos == NULL) {
936 if (counter == 0) {
937 return NULL;
938 }
939 return prev_pos;
940 }
941 counter++;
942 prev_pos = pos;
943 pos += len;
944 if (*pos == '\0') {
945 return prev_pos;
946 }
947 }
948}
949
950void curlhelp_freereadbuffer(curlhelp_read_curlbuf *buf) {
951 free(buf->buf);
952 buf->buf = NULL;
953}
954
955void curlhelp_freewritebuffer(curlhelp_write_curlbuf *buf) {
956 free(buf->buf);
957 buf->buf = NULL;
958}
959
960int curlhelp_initreadbuffer(curlhelp_read_curlbuf **buf, const char *data, size_t datalen) {
961 if ((*buf = calloc(1, sizeof(curlhelp_read_curlbuf))) == NULL) {
962 return 1;
963 }
964
965 (*buf)->buflen = datalen;
966 (*buf)->buf = (char *)calloc((*buf)->buflen, sizeof(char));
967 if ((*buf)->buf == NULL) {
968 return -1;
969 }
970 memcpy((*buf)->buf, data, datalen);
971 (*buf)->pos = 0;
972 return 0;
973}
974
975size_t curlhelp_buffer_read_callback(void *buffer, size_t size, size_t nmemb, void *stream) {
976 curlhelp_read_curlbuf *buf = (curlhelp_read_curlbuf *)stream;
977
978 size_t minimalSize = min(nmemb * size, buf->buflen - buf->pos);
979
980 memcpy(buffer, buf->buf + buf->pos, minimalSize);
981 buf->pos += minimalSize;
982
983 return minimalSize;
984}
985
986int curlhelp_initwritebuffer(curlhelp_write_curlbuf **buf) {
987 if ((*buf = calloc(1, sizeof(curlhelp_write_curlbuf))) == NULL) {
988 return 1;
989 }
990 (*buf)->bufsize = DEFAULT_BUFFER_SIZE * sizeof(char);
991 (*buf)->buflen = 0;
992 (*buf)->buf = (char *)calloc((*buf)->bufsize, sizeof(char));
993 if ((*buf)->buf == NULL) {
994 return -1;
995 }
996 return 0;
997}
998
999size_t curlhelp_buffer_write_callback(void *buffer, size_t size, size_t nmemb, void *stream) {
1000 curlhelp_write_curlbuf *buf = (curlhelp_write_curlbuf *)stream;
1001
1002 while (buf->bufsize < buf->buflen + size * nmemb + 1) {
1003 buf->bufsize = buf->bufsize * 2;
1004 buf->buf = (char *)realloc(buf->buf, buf->bufsize);
1005 if (buf->buf == NULL) {
1006 fprintf(stderr, "malloc failed (%d) %s\n", errno, strerror(errno));
1007 return 0;
1008 }
1009 }
1010
1011 memcpy(buf->buf + buf->buflen, buffer, size * nmemb);
1012 buf->buflen += size * nmemb;
1013 buf->buf[buf->buflen] = '\0';
1014
1015 return size * nmemb;
1016}
1017
1018void cleanup(check_curl_global_state global_state) {
1019 if (global_state.status_line_initialized) {
1020 curlhelp_free_statusline(global_state.status_line);
1021 }
1022 global_state.status_line_initialized = false;
1023
1024 if (global_state.curl_easy_initialized) {
1025 curl_easy_cleanup(global_state.curl);
1026 }
1027 global_state.curl_easy_initialized = false;
1028
1029 if (global_state.curl_global_initialized) {
1030 curl_global_cleanup();
1031 }
1032 global_state.curl_global_initialized = false;
1033
1034 if (global_state.body_buf_initialized) {
1035 curlhelp_freewritebuffer(global_state.body_buf);
1036 }
1037 global_state.body_buf_initialized = false;
1038
1039 if (global_state.header_buf_initialized) {
1040 curlhelp_freewritebuffer(global_state.header_buf);
1041 }
1042 global_state.header_buf_initialized = false;
1043
1044 if (global_state.put_buf_initialized) {
1045 curlhelp_freereadbuffer(global_state.put_buf);
1046 }
1047 global_state.put_buf_initialized = false;
1048
1049 if (global_state.header_list) {
1050 curl_slist_free_all(global_state.header_list);
1051 }
1052
1053 if (global_state.host) {
1054 curl_slist_free_all(global_state.host);
1055 }
1056}
1057
1058int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family) {
1059 struct addrinfo hints = {
1060 .ai_family = addr_family,
1061 .ai_socktype = SOCK_STREAM,
1062 .ai_flags = AI_CANONNAME,
1063 };
1064
1065 struct addrinfo *result;
1066 int errcode = getaddrinfo(host, NULL, &hints, &result);
1067 if (errcode != 0) {
1068 return errcode;
1069 }
1070
1071 strcpy(buf, "");
1072 struct addrinfo *res = result;
1073
1074 size_t buflen_remaining = buflen - 1;
1075 size_t addrstr_len;
1076 char addrstr[100];
1077 void *ptr = {0};
1078 while (res) {
1079 switch (res->ai_family) {
1080 case AF_INET:
1081 ptr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
1082 break;
1083 case AF_INET6:
1084 ptr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
1085 break;
1086 }
1087
1088 inet_ntop(res->ai_family, ptr, addrstr, 100);
1089 if (verbose >= 1) {
1090 printf("* getaddrinfo IPv%d address: %s\n", res->ai_family == PF_INET6 ? 6 : 4,
1091 addrstr);
1092 }
1093
1094 // Append all IPs to buf as a comma-separated string
1095 addrstr_len = strlen(addrstr);
1096 if (buflen_remaining > addrstr_len + 1) {
1097 if (buf[0] != '\0') {
1098 strncat(buf, ",", buflen_remaining);
1099 buflen_remaining -= 1;
1100 }
1101 strncat(buf, addrstr, buflen_remaining);
1102 buflen_remaining -= addrstr_len;
1103 }
1104
1105 res = res->ai_next;
1106 }
1107
1108 freeaddrinfo(result);
1109
1110 return 0;
1111}
1112
1113/* Checks if the server 'reply' is one of the expected 'statuscodes' */
1114bool expected_statuscode(const char *reply, const char *statuscodes) {
1115 char *expected;
1116
1117 if ((expected = strdup(statuscodes)) == NULL) {
1118 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
1119 }
1120
1121 bool result = false;
1122 for (char *code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) {
1123 if (strstr(reply, code) != NULL) {
1124 result = true;
1125 break;
1126 }
1127 }
1128
1129 free(expected);
1130 return result;
1131}
1132
1133/* returns a string "HTTP/1.x" or "HTTP/2" */
1134char *string_statuscode(int major, int minor) {
1135 static char buf[10];
1136
1137 switch (major) {
1138 case 1:
1139 snprintf(buf, sizeof(buf), "HTTP/%d.%d", major, minor);
1140 break;
1141 case 2:
1142 case 3:
1143 snprintf(buf, sizeof(buf), "HTTP/%d", major);
1144 break;
1145 default:
1146 /* assuming here HTTP/N with N>=4 */
1147 snprintf(buf, sizeof(buf), "HTTP/%d", major);
1148 break;
1149 }
1150
1151 return buf;
1152}
1153
1154/* check whether a file exists */
1155void test_file(char *path) {
1156 if (access(path, R_OK) == 0) {
1157 return;
1158 }
1159 usage2(_("file does not exist or is not readable"), path);
1160}
1161
1162mp_subcheck mp_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn,
1163 int days_till_exp_crit);
1164
1165mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_till_exp,
1166 int crit_days_till_exp) {
1167 mp_subcheck sc_cert_result = mp_subcheck_init();
1168 sc_cert_result = mp_set_subcheck_default_state(sc_cert_result, STATE_OK);
1169
1170#ifdef LIBCURL_FEATURE_SSL
1171 if (is_openssl_callback) {
1172# ifdef USE_OPENSSL
1173 /* check certificate with OpenSSL functions, curl has been built against OpenSSL
1174 * and we actually have OpenSSL in the monitoring tools
1175 */
1176 return mp_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp);
1177# else /* USE_OPENSSL */
1178 xasprintf(&result.output, "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL "
1179 "callback used and not linked against OpenSSL\n");
1180 mp_set_subcheck_state(result, STATE_CRITICAL);
1181# endif /* USE_OPENSSL */
1182 } else {
1183 struct curl_slist *slist;
1184
1185 cert_ptr_union cert_ptr = {0};
1186 cert_ptr.to_info = NULL;
1187 CURLcode res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &cert_ptr.to_info);
1188 if (!res && cert_ptr.to_info) {
1189# ifdef USE_OPENSSL
1190 /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert
1191 * parsing We only check the first certificate and assume it's the one of
1192 * the server
1193 */
1194 char *raw_cert = NULL;
1195 bool got_first_cert = false;
1196 for (int i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) {
1197 if (got_first_cert) {
1198 break;
1199 }
1200
1201 for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) {
1202 if (verbose >= 2) {
1203 printf("%d ** %s\n", i, slist->data);
1204 }
1205 if (strncmp(slist->data, "Cert:", 5) == 0) {
1206 raw_cert = &slist->data[5];
1207 got_first_cert = true;
1208 break;
1209 }
1210 }
1211 }
1212
1213 if (!raw_cert) {
1214
1215 xasprintf(&sc_cert_result.output,
1216 _("Cannot retrieve certificates from CERTINFO information - "
1217 "certificate data was empty"));
1218 sc_cert_result = mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL);
1219 return sc_cert_result;
1220 }
1221
1222 BIO *cert_BIO = BIO_new(BIO_s_mem());
1223 BIO_write(cert_BIO, raw_cert, (int)strlen(raw_cert));
1224
1225 cert = PEM_read_bio_X509(cert_BIO, NULL, NULL, NULL);
1226 if (!cert) {
1227 xasprintf(&sc_cert_result.output,
1228 _("Cannot read certificate from CERTINFO information - BIO error"));
1229 sc_cert_result = mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL);
1230 return sc_cert_result;
1231 }
1232
1233 BIO_free(cert_BIO);
1234 return mp_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp);
1235# else /* USE_OPENSSL */
1236 /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our
1237 * disposal, so we use the libcurl CURLINFO data
1238 */
1239 return net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn,
1240 days_till_exp_crit);
1241# endif /* USE_OPENSSL */
1242 } else {
1243 xasprintf(&sc_cert_result.output,
1244 _("Cannot retrieve certificates - cURL returned %d - %s"), res,
1245 curl_easy_strerror(res));
1246 mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL);
1247 }
1248 }
1249#endif /* LIBCURL_FEATURE_SSL */
1250
1251 return sc_cert_result;
1252}
1253
1254char *fmt_url(check_curl_working_state workingState) {
1255 char *url = calloc(DEFAULT_BUFFER_SIZE, sizeof(char));
1256 if (url == NULL) {
1257 die(STATE_UNKNOWN, "memory allocation failed");
1258 }
1259
1260 snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", workingState.use_ssl ? "https" : "http",
1261 (workingState.use_ssl & (workingState.host_name != NULL))
1262 ? workingState.host_name
1263 : workingState.server_address,
1264 workingState.serverPort, workingState.server_url);
1265
1266 return url;
1267}
diff --git a/plugins/check_curl.d/check_curl_helpers.h b/plugins/check_curl.d/check_curl_helpers.h
new file mode 100644
index 00000000..87e45a9d
--- /dev/null
+++ b/plugins/check_curl.d/check_curl_helpers.h
@@ -0,0 +1,124 @@
1#include "./config.h"
2#include <curl/curl.h>
3#include "../picohttpparser/picohttpparser.h"
4#include "output.h"
5
6#if defined(HAVE_SSL) && defined(USE_OPENSSL)
7# include <openssl/opensslv.h>
8#endif
9
10/* for buffers for header and body */
11typedef struct {
12 size_t buflen;
13 size_t bufsize;
14 char *buf;
15} curlhelp_write_curlbuf;
16
17/* for buffering the data sent in PUT */
18typedef struct {
19 size_t buflen;
20 off_t pos;
21 char *buf;
22} curlhelp_read_curlbuf;
23
24/* for parsing the HTTP status line */
25typedef struct {
26 int http_major; /* major version of the protocol, always 1 (HTTP/0.9
27 * never reached the big internet most likely) */
28 int http_minor; /* minor version of the protocol, usually 0 or 1 */
29 int http_code; /* HTTP return code as in RFC 2145 */
30 int http_subcode; /* Microsoft IIS extension, HTTP subcodes, see
31 * http://support.microsoft.com/kb/318380/en-us */
32 const char *msg; /* the human readable message */
33 char *first_line; /* a copy of the first line */
34} curlhelp_statusline;
35
36typedef struct {
37 bool curl_global_initialized;
38 bool curl_easy_initialized;
39
40 bool body_buf_initialized;
41 curlhelp_write_curlbuf *body_buf;
42
43 bool header_buf_initialized;
44 curlhelp_write_curlbuf *header_buf;
45
46 bool status_line_initialized;
47 curlhelp_statusline *status_line;
48
49 bool put_buf_initialized;
50 curlhelp_read_curlbuf *put_buf;
51
52 CURL *curl;
53
54 struct curl_slist *header_list;
55 struct curl_slist *host;
56} check_curl_global_state;
57
58/* to know the underlying SSL library used by libcurl */
59typedef enum curlhelp_ssl_library {
60 CURLHELP_SSL_LIBRARY_UNKNOWN,
61 CURLHELP_SSL_LIBRARY_OPENSSL,
62 CURLHELP_SSL_LIBRARY_LIBRESSL,
63 CURLHELP_SSL_LIBRARY_GNUTLS,
64 CURLHELP_SSL_LIBRARY_NSS
65} curlhelp_ssl_library;
66
67#define MAKE_LIBCURL_VERSION(major, minor, patch) ((major) * 0x10000 + (minor) * 0x100 + (patch))
68
69typedef struct {
70 int errorcode;
71 check_curl_global_state curl_state;
72 check_curl_working_state working_state;
73} check_curl_configure_curl_wrapper;
74
75check_curl_configure_curl_wrapper check_curl_configure_curl(check_curl_static_curl_config config,
76 check_curl_working_state working_state,
77 bool check_cert,
78 bool on_redirect_dependent,
79 int follow_method, int max_depth);
80
81void handle_curl_option_return_code(CURLcode res, const char *option);
82
83int curlhelp_initwritebuffer(curlhelp_write_curlbuf **buf);
84size_t curlhelp_buffer_write_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/,
85 void * /*stream*/);
86void curlhelp_freewritebuffer(curlhelp_write_curlbuf * /*buf*/);
87
88int curlhelp_initreadbuffer(curlhelp_read_curlbuf **buf, const char * /*data*/, size_t /*datalen*/);
89size_t curlhelp_buffer_read_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/,
90 void * /*stream*/);
91void curlhelp_freereadbuffer(curlhelp_read_curlbuf * /*buf*/);
92
93curlhelp_ssl_library curlhelp_get_ssl_library(void);
94const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library /*ssl_library*/);
95
96typedef union {
97 struct curl_slist *to_info;
98 struct curl_certinfo *to_certinfo;
99} cert_ptr_union;
100int net_noopenssl_check_certificate(cert_ptr_union *, int, int);
101
102int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * /*status_line*/);
103void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/);
104
105char *get_header_value(const struct phr_header *headers, size_t nof_headers, const char *header);
106mp_subcheck check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/,
107 int /*maximum_age*/);
108size_t get_content_length(const curlhelp_write_curlbuf *header_buf,
109 const curlhelp_write_curlbuf *body_buf);
110int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family);
111CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm);
112
113#define INET_ADDR_MAX_SIZE INET6_ADDRSTRLEN
114const char *strrstr2(const char *haystack, const char *needle);
115
116void cleanup(check_curl_global_state global_state);
117
118bool expected_statuscode(const char *reply, const char *statuscodes);
119char *string_statuscode(int major, int minor);
120
121void test_file(char *path);
122mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_till_exp,
123 int crit_days_till_exp);
124char *fmt_url(check_curl_working_state workingState);
diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h
new file mode 100644
index 00000000..f51b2ee9
--- /dev/null
+++ b/plugins/check_curl.d/config.h
@@ -0,0 +1,116 @@
1#pragma once
2
3#include "../../config.h"
4#include "../common.h"
5#include "../../lib/states.h"
6#include "../../lib/thresholds.h"
7#include <stddef.h>
8#include <string.h>
9#include <sys/socket.h>
10#include "curl/curl.h"
11#include "perfdata.h"
12#include "regex.h"
13
14enum {
15 MAX_RE_SIZE = 1024,
16 HTTP_PORT = 80,
17 HTTPS_PORT = 443,
18 MAX_PORT = 65535,
19 DEFAULT_MAX_REDIRS = 15
20};
21
22enum {
23 FOLLOW_HTTP_CURL = 0,
24 FOLLOW_LIBCURL = 1
25};
26
27enum {
28 STICKY_NONE = 0,
29 STICKY_HOST = 1,
30 STICKY_PORT = 2
31};
32
33#define HTTP_EXPECT "HTTP/"
34#define DEFAULT_BUFFER_SIZE 2048
35#define DEFAULT_SERVER_URL "/"
36
37typedef struct {
38 char *server_address;
39 char *server_url;
40 char *host_name;
41
42 char *http_method;
43
44 char *http_post_data;
45
46 unsigned short virtualPort;
47 unsigned short serverPort;
48
49 bool use_ssl;
50 bool no_body;
51} check_curl_working_state;
52
53check_curl_working_state check_curl_working_state_init();
54
55typedef struct {
56 bool automatic_decompression;
57 bool haproxy_protocol;
58 long socket_timeout;
59 sa_family_t sin_family;
60 int curl_http_version;
61 char **http_opt_headers;
62 size_t http_opt_headers_count;
63 int ssl_version;
64 char *client_cert;
65 char *client_privkey;
66 char *ca_cert;
67 bool verify_peer_and_host;
68 char user_agent[DEFAULT_BUFFER_SIZE];
69 char proxy_auth[MAX_INPUT_BUFFER];
70 char user_auth[MAX_INPUT_BUFFER];
71 char *http_content_type;
72 char *cookie_jar_file;
73} check_curl_static_curl_config;
74
75typedef struct {
76 check_curl_working_state initial_config;
77
78 check_curl_static_curl_config curl_config;
79 int max_depth;
80 int followmethod;
81 int followsticky;
82
83 int maximum_age;
84
85 // the original regex string from the command line
86 char regexp[MAX_RE_SIZE];
87
88 // the compiled regex for usage later
89 regex_t compiled_regex;
90
91 mp_state_enum state_regex;
92 bool invert_regex;
93 bool check_cert;
94 bool continue_after_check_cert;
95 int days_till_exp_warn;
96 int days_till_exp_crit;
97 mp_thresholds thlds;
98 mp_range page_length_limits;
99 bool page_length_limits_is_set;
100 struct {
101 char string[MAX_INPUT_BUFFER];
102 bool is_present;
103 } server_expect;
104 char string_expect[MAX_INPUT_BUFFER];
105 char header_expect[MAX_INPUT_BUFFER];
106 mp_state_enum on_redirect_result_state;
107 bool on_redirect_dependent;
108
109 bool show_extended_perfdata;
110 bool show_body;
111
112 bool output_format_is_set;
113 mp_output_format output_format;
114} check_curl_config;
115
116check_curl_config check_curl_config_init();
diff --git a/plugins/netutils.h b/plugins/netutils.h
index a95057e0..6adb8e01 100644
--- a/plugins/netutils.h
+++ b/plugins/netutils.h
@@ -1,63 +1,65 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring Plugins net utilities include file 3 * Monitoring Plugins net utilities include file
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999 Ethan Galstad (nagios@nagios.org) 6 * Copyright (c) 1999 Ethan Galstad (nagios@nagios.org)
7* Copyright (c) 2003-2007 Monitoring Plugins Development Team 7 * Copyright (c) 2003-2007 Monitoring Plugins Development Team
8* 8 *
9* Description: 9 * Description:
10* 10 *
11* This file contains common include files and function definitions 11 * This file contains common include files and function definitions
12* used in many of the plugins. 12 * used in many of the plugins.
13* 13 *
14* 14 *
15* This program is free software: you can redistribute it and/or modify 15 * This program is free software: you can redistribute it and/or modify
16* it under the terms of the GNU General Public License as published by 16 * it under the terms of the GNU General Public License as published by
17* the Free Software Foundation, either version 3 of the License, or 17 * the Free Software Foundation, either version 3 of the License, or
18* (at your option) any later version. 18 * (at your option) any later version.
19* 19 *
20* This program is distributed in the hope that it will be useful, 20 * This program is distributed in the hope that it will be useful,
21* but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23* GNU General Public License for more details. 23 * GNU General Public License for more details.
24* 24 *
25* You should have received a copy of the GNU General Public License 25 * You should have received a copy of the GNU General Public License
26* along with this program. If not, see <http://www.gnu.org/licenses/>. 26 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27* 27 *
28* 28 *
29*****************************************************************************/ 29 *****************************************************************************/
30 30
31#ifndef _NETUTILS_H_ 31#ifndef _NETUTILS_H_
32#define _NETUTILS_H_ 32#define _NETUTILS_H_
33 33
34#include "common.h" 34#include "common.h"
35#include "output.h"
36#include "states.h"
35#include "utils.h" 37#include "utils.h"
36#include <netinet/in.h> 38#include <netinet/in.h>
37#include <arpa/inet.h> 39#include <arpa/inet.h>
38#include <netdb.h> 40#include <netdb.h>
39 41
40#ifdef HAVE_SYS_UN_H 42#ifdef HAVE_SYS_UN_H
41# include <sys/un.h> 43# include <sys/un.h>
42# ifndef UNIX_PATH_MAX 44# ifndef UNIX_PATH_MAX
43 /* linux uses this, on sun it's hard-coded at 108 without a define, on BSD at 104 */ 45/* linux uses this, on sun it's hard-coded at 108 without a define, on BSD at 104 */
44# define UNIX_PATH_MAX 104 46# define UNIX_PATH_MAX 104
45# endif /* UNIX_PATH_MAX */ 47# endif /* UNIX_PATH_MAX */
46#endif /* HAVE_SYS_UN_H */ 48#endif /* HAVE_SYS_UN_H */
47 49
48#ifndef HOST_MAX_BYTES 50#ifndef HOST_MAX_BYTES
49# define HOST_MAX_BYTES 255 51# define HOST_MAX_BYTES 255
50#endif 52#endif
51 53
52/* process_request and wrapper macros */ 54/* process_request and wrapper macros */
53#define process_tcp_request(addr, port, sbuf, rbuf, rsize) \ 55#define process_tcp_request(addr, port, sbuf, rbuf, rsize) \
54 process_request(addr, port, IPPROTO_TCP, sbuf, rbuf, rsize) 56 process_request(addr, port, IPPROTO_TCP, sbuf, rbuf, rsize)
55#define process_udp_request(addr, port, sbuf, rbuf, rsize) \ 57#define process_udp_request(addr, port, sbuf, rbuf, rsize) \
56 process_request(addr, port, IPPROTO_UDP, sbuf, rbuf, rsize) 58 process_request(addr, port, IPPROTO_UDP, sbuf, rbuf, rsize)
57int process_tcp_request2 (const char *address, int port, 59int process_tcp_request2(const char *address, int port, const char *sbuffer, char *rbuffer,
58 const char *sbuffer, char *rbuffer, int rsize); 60 int rsize);
59int process_request (const char *address, int port, int proto, 61int process_request(const char *address, int port, int proto, const char *sbuffer, char *rbuffer,
60 const char *sbuffer, char *rbuffer, int rsize); 62 int rsize);
61 63
62/* my_connect and wrapper macros */ 64/* my_connect and wrapper macros */
63#define my_tcp_connect(addr, port, s) np_net_connect(addr, port, s, IPPROTO_TCP) 65#define my_tcp_connect(addr, port, s) np_net_connect(addr, port, s, IPPROTO_TCP)
@@ -65,25 +67,22 @@ int process_request (const char *address, int port, int proto,
65int np_net_connect(const char *address, int port, int *sd, int proto); 67int np_net_connect(const char *address, int port, int *sd, int proto);
66 68
67/* send_request and wrapper macros */ 69/* send_request and wrapper macros */
68#define send_tcp_request(s, sbuf, rbuf, rsize) \ 70#define send_tcp_request(s, sbuf, rbuf, rsize) send_request(s, IPPROTO_TCP, sbuf, rbuf, rsize)
69 send_request(s, IPPROTO_TCP, sbuf, rbuf, rsize) 71#define send_udp_request(s, sbuf, rbuf, rsize) send_request(s, IPPROTO_UDP, sbuf, rbuf, rsize)
70#define send_udp_request(s, sbuf, rbuf, rsize) \ 72int send_request(int sd, int proto, const char *send_buffer, char *recv_buffer, int recv_size);
71 send_request(s, IPPROTO_UDP, sbuf, rbuf, rsize)
72int send_request (int sd, int proto, const char *send_buffer, char *recv_buffer, int recv_size);
73
74 73
75/* "is_*" wrapper macros and functions */ 74/* "is_*" wrapper macros and functions */
76bool is_host (const char *); 75bool is_host(const char *);
77bool is_addr (const char *); 76bool is_addr(const char *);
78int dns_lookup (const char *, struct sockaddr_storage *, int); 77int dns_lookup(const char *, struct sockaddr_storage *, int);
79void host_or_die(const char *str); 78void host_or_die(const char *str);
80#define resolve_host_or_addr(addr, family) dns_lookup(addr, NULL, family) 79#define resolve_host_or_addr(addr, family) dns_lookup(addr, NULL, family)
81#define is_inet_addr(addr) resolve_host_or_addr(addr, AF_INET) 80#define is_inet_addr(addr) resolve_host_or_addr(addr, AF_INET)
82#ifdef USE_IPV6 81#ifdef USE_IPV6
83# define is_inet6_addr(addr) resolve_host_or_addr(addr, AF_INET6) 82# define is_inet6_addr(addr) resolve_host_or_addr(addr, AF_INET6)
84# define is_hostname(addr) resolve_host_or_addr(addr, address_family) 83# define is_hostname(addr) resolve_host_or_addr(addr, address_family)
85#else 84#else
86# define is_hostname(addr) resolve_host_or_addr(addr, AF_INET) 85# define is_hostname(addr) resolve_host_or_addr(addr, AF_INET)
87#endif 86#endif
88 87
89extern unsigned int socket_timeout; 88extern unsigned int socket_timeout;
@@ -92,29 +91,30 @@ extern int econn_refuse_state;
92extern bool was_refused; 91extern bool was_refused;
93extern int address_family; 92extern int address_family;
94 93
95void socket_timeout_alarm_handler (int) __attribute__((noreturn)); 94void socket_timeout_alarm_handler(int) __attribute__((noreturn));
96 95
97/* SSL-Related functionality */ 96/* SSL-Related functionality */
98#ifdef HAVE_SSL 97#ifdef HAVE_SSL
99# define MP_SSLv2 1 98# define MP_SSLv2 1
100# define MP_SSLv3 2 99# define MP_SSLv3 2
101# define MP_TLSv1 3 100# define MP_TLSv1 3
102# define MP_TLSv1_1 4 101# define MP_TLSv1_1 4
103# define MP_TLSv1_2 5 102# define MP_TLSv1_2 5
104# define MP_SSLv2_OR_NEWER 6 103# define MP_SSLv2_OR_NEWER 6
105# define MP_SSLv3_OR_NEWER 7 104# define MP_SSLv3_OR_NEWER 7
106# define MP_TLSv1_OR_NEWER 8 105# define MP_TLSv1_OR_NEWER 8
107# define MP_TLSv1_1_OR_NEWER 9 106# define MP_TLSv1_1_OR_NEWER 9
108# define MP_TLSv1_2_OR_NEWER 10 107# define MP_TLSv1_2_OR_NEWER 10
109/* maybe this could be merged with the above np_net_connect, via some flags */ 108/* maybe this could be merged with the above np_net_connect, via some flags */
110int np_net_ssl_init(int sd); 109int np_net_ssl_init(int sd);
111int np_net_ssl_init_with_hostname(int sd, char *host_name); 110int np_net_ssl_init_with_hostname(int sd, char *host_name);
112int np_net_ssl_init_with_hostname_and_version(int sd, char *host_name, int version); 111int np_net_ssl_init_with_hostname_and_version(int sd, char *host_name, int version);
113int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int version, char *cert, char *privkey); 112int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int version, char *cert,
113 char *privkey);
114void np_net_ssl_cleanup(); 114void np_net_ssl_cleanup();
115int np_net_ssl_write(const void *buf, int num); 115int np_net_ssl_write(const void *buf, int num);
116int np_net_ssl_read(void *buf, int num); 116int np_net_ssl_read(void *buf, int num);
117int np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); 117mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit);
118mp_subcheck mp_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit);
118#endif /* HAVE_SSL */ 119#endif /* HAVE_SSL */
119
120#endif /* _NETUTILS_H_ */ 120#endif /* _NETUTILS_H_ */
diff --git a/plugins/sslutils.c b/plugins/sslutils.c
index 96740b3a..3ce6afed 100644
--- a/plugins/sslutils.c
+++ b/plugins/sslutils.c
@@ -26,10 +26,12 @@
26 * 26 *
27 *****************************************************************************/ 27 *****************************************************************************/
28 28
29#include "output.h"
29#define MAX_CN_LENGTH 256 30#define MAX_CN_LENGTH 256
30#include "common.h" 31#include "common.h"
31#include "netutils.h" 32#include "netutils.h"
32#include "../lib/monitoringplug.h" 33#include "../lib/monitoringplug.h"
34#include "states.h"
33 35
34#ifdef HAVE_SSL 36#ifdef HAVE_SSL
35static SSL_CTX *ctx = NULL; 37static SSL_CTX *ctx = NULL;
@@ -37,13 +39,16 @@ static SSL *s = NULL;
37 39
38int np_net_ssl_init(int sd) { return np_net_ssl_init_with_hostname(sd, NULL); } 40int np_net_ssl_init(int sd) { return np_net_ssl_init_with_hostname(sd, NULL); }
39 41
40int np_net_ssl_init_with_hostname(int sd, char *host_name) { return np_net_ssl_init_with_hostname_and_version(sd, host_name, 0); } 42int np_net_ssl_init_with_hostname(int sd, char *host_name) {
43 return np_net_ssl_init_with_hostname_and_version(sd, host_name, 0);
44}
41 45
42int np_net_ssl_init_with_hostname_and_version(int sd, char *host_name, int version) { 46int np_net_ssl_init_with_hostname_and_version(int sd, char *host_name, int version) {
43 return np_net_ssl_init_with_hostname_version_and_cert(sd, host_name, version, NULL, NULL); 47 return np_net_ssl_init_with_hostname_version_and_cert(sd, host_name, version, NULL, NULL);
44} 48}
45 49
46int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int version, char *cert, char *privkey) { 50int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int version, char *cert,
51 char *privkey) {
47 long options = 0; 52 long options = 0;
48 53
49 if ((ctx = SSL_CTX_new(TLS_client_method())) == NULL) { 54 if ((ctx = SSL_CTX_new(TLS_client_method())) == NULL) {
@@ -75,7 +80,8 @@ int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int
75# endif 80# endif
76 case MP_TLSv1_1: /* TLSv1.1 protocol */ 81 case MP_TLSv1_1: /* TLSv1.1 protocol */
77# if !defined(SSL_OP_NO_TLSv1_1) 82# if !defined(SSL_OP_NO_TLSv1_1)
78 printf("%s\n", _("UNKNOWN - TLS protocol version 1.1 is not supported by your SSL library.")); 83 printf("%s\n",
84 _("UNKNOWN - TLS protocol version 1.1 is not supported by your SSL library."));
79 return STATE_UNKNOWN; 85 return STATE_UNKNOWN;
80# else 86# else
81 SSL_CTX_set_min_proto_version(ctx, TLS1_1_VERSION); 87 SSL_CTX_set_min_proto_version(ctx, TLS1_1_VERSION);
@@ -84,7 +90,8 @@ int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int
84# endif 90# endif
85 case MP_TLSv1_2: /* TLSv1.2 protocol */ 91 case MP_TLSv1_2: /* TLSv1.2 protocol */
86# if !defined(SSL_OP_NO_TLSv1_2) 92# if !defined(SSL_OP_NO_TLSv1_2)
87 printf("%s\n", _("UNKNOWN - TLS protocol version 1.2 is not supported by your SSL library.")); 93 printf("%s\n",
94 _("UNKNOWN - TLS protocol version 1.2 is not supported by your SSL library."));
88 return STATE_UNKNOWN; 95 return STATE_UNKNOWN;
89# else 96# else
90 SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); 97 SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
@@ -145,8 +152,9 @@ int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int
145 SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); 152 SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
146 if ((s = SSL_new(ctx)) != NULL) { 153 if ((s = SSL_new(ctx)) != NULL) {
147# ifdef SSL_set_tlsext_host_name 154# ifdef SSL_set_tlsext_host_name
148 if (host_name != NULL) 155 if (host_name != NULL) {
149 SSL_set_tlsext_host_name(s, host_name); 156 SSL_set_tlsext_host_name(s, host_name);
157 }
150# endif 158# endif
151 SSL_set_fd(s, sd); 159 SSL_set_fd(s, sd);
152 if (SSL_connect(s) == 1) { 160 if (SSL_connect(s) == 1) {
@@ -182,63 +190,54 @@ int np_net_ssl_write(const void *buf, int num) { return SSL_write(s, buf, num);
182 190
183int np_net_ssl_read(void *buf, int num) { return SSL_read(s, buf, num); } 191int np_net_ssl_read(void *buf, int num) { return SSL_read(s, buf, num); }
184 192
185int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit) { 193mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn,
194 int days_till_exp_crit) {
186# ifdef USE_OPENSSL 195# ifdef USE_OPENSSL
187 X509_NAME *subj = NULL;
188 char timestamp[50] = "";
189 char cn[MAX_CN_LENGTH] = "";
190 char *tz;
191
192 int cnlen = -1;
193 int status = STATE_UNKNOWN;
194
195 ASN1_STRING *tm;
196 int offset;
197 struct tm stamp;
198 float time_left;
199 int days_left;
200 int time_remaining;
201 time_t tm_t;
202
203 if (!certificate) { 196 if (!certificate) {
204 printf("%s\n", _("CRITICAL - No server certificate present to inspect.")); 197 printf("%s\n", _("CRITICAL - No server certificate present to inspect."));
205 return STATE_CRITICAL; 198 return STATE_CRITICAL;
206 } 199 }
207 200
208 /* Extract CN from certificate subject */ 201 /* Extract CN from certificate subject */
209 subj = X509_get_subject_name(certificate); 202 X509_NAME *subj = X509_get_subject_name(certificate);
210 203
211 if (!subj) { 204 if (!subj) {
212 printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject.")); 205 printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject."));
213 return STATE_CRITICAL; 206 return STATE_CRITICAL;
214 } 207 }
215 cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, cn, sizeof(cn)); 208
216 if (cnlen == -1) 209 char cn[MAX_CN_LENGTH] = "";
210 int cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, cn, sizeof(cn));
211 if (cnlen == -1) {
217 strcpy(cn, _("Unknown CN")); 212 strcpy(cn, _("Unknown CN"));
213 }
218 214
219 /* Retrieve timestamp of certificate */ 215 /* Retrieve timestamp of certificate */
220 tm = X509_get_notAfter(certificate); 216 ASN1_STRING *tm = X509_get_notAfter(certificate);
221 217
218 int offset = 0;
219 struct tm stamp = {};
222 /* Generate tm structure to process timestamp */ 220 /* Generate tm structure to process timestamp */
223 if (tm->type == V_ASN1_UTCTIME) { 221 if (tm->type == V_ASN1_UTCTIME) {
224 if (tm->length < 10) { 222 if (tm->length < 10) {
225 printf("%s\n", _("CRITICAL - Wrong time format in certificate.")); 223 printf("%s\n", _("CRITICAL - Wrong time format in certificate."));
226 return STATE_CRITICAL; 224 return STATE_CRITICAL;
227 } else {
228 stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
229 if (stamp.tm_year < 50)
230 stamp.tm_year += 100;
231 offset = 0;
232 } 225 }
226 stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
227 if (stamp.tm_year < 50) {
228 stamp.tm_year += 100;
229 }
230 offset = 0;
231
233 } else { 232 } else {
234 if (tm->length < 12) { 233 if (tm->length < 12) {
235 printf("%s\n", _("CRITICAL - Wrong time format in certificate.")); 234 printf("%s\n", _("CRITICAL - Wrong time format in certificate."));
236 return STATE_CRITICAL; 235 return STATE_CRITICAL;
237 } else {
238 stamp.tm_year = (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 + (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
239 stamp.tm_year -= 1900;
240 offset = 2;
241 } 236 }
237 stamp.tm_year = (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
238 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
239 stamp.tm_year -= 1900;
240 offset = 2;
242 } 241 }
243 stamp.tm_mon = (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1; 242 stamp.tm_mon = (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
244 stamp.tm_mday = (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0'); 243 stamp.tm_mday = (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
@@ -247,48 +246,60 @@ int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int
247 stamp.tm_sec = (tm->data[10 + offset] - '0') * 10 + (tm->data[11 + offset] - '0'); 246 stamp.tm_sec = (tm->data[10 + offset] - '0') * 10 + (tm->data[11 + offset] - '0');
248 stamp.tm_isdst = -1; 247 stamp.tm_isdst = -1;
249 248
250 tm_t = timegm(&stamp); 249 time_t tm_t = timegm(&stamp);
251 time_left = difftime(tm_t, time(NULL)); 250 float time_left = difftime(tm_t, time(NULL));
252 days_left = time_left / 86400; 251 int days_left = time_left / 86400;
253 tz = getenv("TZ"); 252 char *tz = getenv("TZ");
254 setenv("TZ", "GMT", 1); 253 setenv("TZ", "GMT", 1);
255 tzset(); 254 tzset();
255
256 char timestamp[50] = "";
256 strftime(timestamp, 50, "%c %z", localtime(&tm_t)); 257 strftime(timestamp, 50, "%c %z", localtime(&tm_t));
257 if (tz) 258 if (tz) {
258 setenv("TZ", tz, 1); 259 setenv("TZ", tz, 1);
259 else 260 } else {
260 unsetenv("TZ"); 261 unsetenv("TZ");
262 }
263
261 tzset(); 264 tzset();
262 265
266 int time_remaining;
267 mp_state_enum status = STATE_UNKNOWN;
263 if (days_left > 0 && days_left <= days_till_exp_warn) { 268 if (days_left > 0 && days_left <= days_till_exp_warn) {
264 printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, 269 printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"),
265 days_left, timestamp); 270 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, days_left, timestamp);
266 if (days_left > days_till_exp_crit) 271 if (days_left > days_till_exp_crit) {
267 status = STATE_WARNING; 272 status = STATE_WARNING;
268 else 273 } else {
269 status = STATE_CRITICAL; 274 status = STATE_CRITICAL;
275 }
270 } else if (days_left == 0 && time_left > 0) { 276 } else if (days_left == 0 && time_left > 0) {
271 if (time_left >= 3600) 277 if (time_left >= 3600) {
272 time_remaining = (int)time_left / 3600; 278 time_remaining = (int)time_left / 3600;
273 else 279 } else {
274 time_remaining = (int)time_left / 60; 280 time_remaining = (int)time_left / 60;
281 }
275 282
276 printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, 283 printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"),
277 time_remaining, time_left >= 3600 ? "hours" : "minutes", timestamp); 284 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, time_remaining,
285 time_left >= 3600 ? "hours" : "minutes", timestamp);
278 286
279 if (days_left > days_till_exp_crit) 287 if (days_left > days_till_exp_crit) {
280 status = STATE_WARNING; 288 status = STATE_WARNING;
281 else 289 } else {
282 status = STATE_CRITICAL; 290 status = STATE_CRITICAL;
291 }
283 } else if (time_left < 0) { 292 } else if (time_left < 0) {
284 printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), cn, timestamp); 293 printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), cn, timestamp);
285 status = STATE_CRITICAL; 294 status = STATE_CRITICAL;
286 } else if (days_left == 0) { 295 } else if (days_left == 0) {
287 printf(_("%s - Certificate '%s' just expired (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, timestamp); 296 printf(_("%s - Certificate '%s' just expired (%s).\n"),
288 if (days_left > days_till_exp_crit) 297 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, timestamp);
298 if (days_left > days_till_exp_crit) {
289 status = STATE_WARNING; 299 status = STATE_WARNING;
290 else 300 } else {
291 status = STATE_CRITICAL; 301 status = STATE_CRITICAL;
302 }
292 } else { 303 } else {
293 printf(_("OK - Certificate '%s' will expire on %s.\n"), cn, timestamp); 304 printf(_("OK - Certificate '%s' will expire on %s.\n"), cn, timestamp);
294 status = STATE_OK; 305 status = STATE_OK;
@@ -301,7 +312,7 @@ int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int
301# endif /* USE_OPENSSL */ 312# endif /* USE_OPENSSL */
302} 313}
303 314
304int np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit) { 315mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit) {
305# ifdef USE_OPENSSL 316# ifdef USE_OPENSSL
306 X509 *certificate = NULL; 317 X509 *certificate = NULL;
307 certificate = SSL_get_peer_certificate(s); 318 certificate = SSL_get_peer_certificate(s);
@@ -312,4 +323,138 @@ int np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit) {
312# endif /* USE_OPENSSL */ 323# endif /* USE_OPENSSL */
313} 324}
314 325
326mp_subcheck mp_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn,
327 int days_till_exp_crit) {
328 mp_subcheck sc_cert = mp_subcheck_init();
329# ifdef USE_OPENSSL
330 if (!certificate) {
331 xasprintf(&sc_cert.output, _("No server certificate present to inspect"));
332 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
333 return sc_cert;
334 }
335
336 /* Extract CN from certificate subject */
337 X509_NAME *subj = X509_get_subject_name(certificate);
338
339 if (!subj) {
340 xasprintf(&sc_cert.output, _("Cannot retrieve certificate subject"));
341 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
342 return sc_cert;
343 }
344
345 char commonName[MAX_CN_LENGTH] = "";
346 int cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, commonName, sizeof(commonName));
347 if (cnlen == -1) {
348 strcpy(commonName, _("Unknown CN"));
349 }
350
351 /* Retrieve timestamp of certificate */
352 ASN1_STRING *expiry_timestamp = X509_get_notAfter(certificate);
353
354 int offset = 0;
355 struct tm stamp = {};
356 /* Generate tm structure to process timestamp */
357 if (expiry_timestamp->type == V_ASN1_UTCTIME) {
358 if (expiry_timestamp->length < 10) {
359 xasprintf(&sc_cert.output, _("Wrong time format in certificate"));
360 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
361 return sc_cert;
362 }
363
364 stamp.tm_year = (expiry_timestamp->data[0] - '0') * 10 + (expiry_timestamp->data[1] - '0');
365 if (stamp.tm_year < 50) {
366 stamp.tm_year += 100;
367 }
368
369 offset = 0;
370 } else {
371 if (expiry_timestamp->length < 12) {
372 xasprintf(&sc_cert.output, _("Wrong time format in certificate"));
373 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
374 return sc_cert;
375 }
376 stamp.tm_year = (expiry_timestamp->data[0] - '0') * 1000 +
377 (expiry_timestamp->data[1] - '0') * 100 +
378 (expiry_timestamp->data[2] - '0') * 10 + (expiry_timestamp->data[3] - '0');
379 stamp.tm_year -= 1900;
380 offset = 2;
381 }
382
383 stamp.tm_mon = (expiry_timestamp->data[2 + offset] - '0') * 10 +
384 (expiry_timestamp->data[3 + offset] - '0') - 1;
385 stamp.tm_mday = (expiry_timestamp->data[4 + offset] - '0') * 10 +
386 (expiry_timestamp->data[5 + offset] - '0');
387 stamp.tm_hour = (expiry_timestamp->data[6 + offset] - '0') * 10 +
388 (expiry_timestamp->data[7 + offset] - '0');
389 stamp.tm_min = (expiry_timestamp->data[8 + offset] - '0') * 10 +
390 (expiry_timestamp->data[9 + offset] - '0');
391 stamp.tm_sec = (expiry_timestamp->data[10 + offset] - '0') * 10 +
392 (expiry_timestamp->data[11 + offset] - '0');
393 stamp.tm_isdst = -1;
394
395 time_t tm_t = timegm(&stamp);
396 double time_left = difftime(tm_t, time(NULL));
397 int days_left = (int)(time_left / 86400);
398 char *timeZone = getenv("TZ");
399 setenv("TZ", "GMT", 1);
400 tzset();
401
402 char timestamp[50] = "";
403 strftime(timestamp, 50, "%c %z", localtime(&tm_t));
404 if (timeZone) {
405 setenv("TZ", timeZone, 1);
406 } else {
407 unsetenv("TZ");
408 }
409
410 tzset();
411
412 int time_remaining;
413 if (days_left > 0 && days_left <= days_till_exp_warn) {
414 xasprintf(&sc_cert.output, _("Certificate '%s' expires in %d day(s) (%s)"), commonName,
415 days_left, timestamp);
416 if (days_left > days_till_exp_crit) {
417 sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING);
418 } else {
419 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
420 }
421 } else if (days_left == 0 && time_left > 0) {
422 if (time_left >= 3600) {
423 time_remaining = (int)time_left / 3600;
424 } else {
425 time_remaining = (int)time_left / 60;
426 }
427
428 xasprintf(&sc_cert.output, _("Certificate '%s' expires in %u %s (%s)"), commonName,
429 time_remaining, time_left >= 3600 ? "hours" : "minutes", timestamp);
430
431 if (days_left > days_till_exp_crit) {
432 sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING);
433 } else {
434 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
435 }
436 } else if (time_left < 0) {
437 xasprintf(&sc_cert.output, _("Certificate '%s' expired on %s"), commonName, timestamp);
438 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
439 } else if (days_left == 0) {
440 xasprintf(&sc_cert.output, _("Certificate '%s' just expired (%s)"), commonName,
441 timestamp);
442 if (days_left > days_till_exp_crit) {
443 sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING);
444 } else {
445 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
446 }
447 } else {
448 xasprintf(&sc_cert.output, _("Certificate '%s' will expire on %s"), commonName,
449 timestamp);
450 sc_cert = mp_set_subcheck_state(sc_cert, STATE_OK);
451 }
452 X509_free(certificate);
453 return sc_cert;
454# else /* ifndef USE_OPENSSL */
455 xasprintf(&sc_cert.output, _("Plugin does not support checking certificates"));
456 sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING);
457 return sc_cert;
458# endif /* USE_OPENSSL */
459}
315#endif /* HAVE_SSL */ 460#endif /* HAVE_SSL */
diff --git a/plugins/t/check_curl.t b/plugins/t/check_curl.t
index 7a930a4e..2c2fafde 100644
--- a/plugins/t/check_curl.t
+++ b/plugins/t/check_curl.t
@@ -13,12 +13,12 @@ use vars qw($tests $has_ipv6);
13BEGIN { 13BEGIN {
14 use NPTest; 14 use NPTest;
15 $has_ipv6 = NPTest::has_ipv6(); 15 $has_ipv6 = NPTest::has_ipv6();
16 $tests = $has_ipv6 ? 59 : 57; 16 $tests = $has_ipv6 ? 55 : 53;
17 plan tests => $tests; 17 plan tests => $tests;
18} 18}
19 19
20 20
21my $successOutput = '/OK.*HTTP.*second/'; 21my $successOutput = '/.*HTTP.*second/';
22 22
23my $res; 23my $res;
24my $plugin = 'check_http'; 24my $plugin = 'check_http';
@@ -63,7 +63,7 @@ $res = NPTest->testCmd(
63 ); 63 );
64cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" ); 64cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" );
65# was CRITICAL only, but both check_curl and check_http print HTTP CRITICAL (puzzle?!) 65# was CRITICAL only, but both check_curl and check_http print HTTP CRITICAL (puzzle?!)
66like( $res->output, "/HTTP CRITICAL - Invalid HTTP response received from host on port 80: cURL returned 28 - Connection timed out after/", "Output OK"); 66like( $res->output, "/cURL returned 28 - Connection timed out after/", "Output OK");
67 67
68$res = NPTest->testCmd( 68$res = NPTest->testCmd(
69 "./$plugin $hostname_invalid -wt 1 -ct 2" 69 "./$plugin $hostname_invalid -wt 1 -ct 2"
@@ -124,14 +124,14 @@ SKIP: {
124 124
125 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing'" ); 125 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing'" );
126 cmp_ok( $res->return_code, "==", 2, "Not got 'mONiTORing'"); 126 cmp_ok( $res->return_code, "==", 2, "Not got 'mONiTORing'");
127 like ( $res->output, "/pattern not found/", "Error message says 'pattern not found'"); 127 like ( $res->output, "/matched not/", "Error message says 'matched not'");
128 128
129 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -R 'mONiTORing'" ); 129 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -R 'mONiTORing'" );
130 cmp_ok( $res->return_code, "==", 0, "But case insensitive doesn't mind 'mONiTORing'"); 130 cmp_ok( $res->return_code, "==", 0, "But case insensitive doesn't mind 'mONiTORing'");
131 131
132 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'monitoring' --invert-regex" ); 132 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'monitoring' --invert-regex" );
133 cmp_ok( $res->return_code, "==", 2, "Invert results work when found"); 133 cmp_ok( $res->return_code, "==", 2, "Invert results work when found");
134 like ( $res->output, "/pattern found/", "Error message says 'pattern found'"); 134 like ( $res->output, "/matched/", "Error message says 'matched'");
135 135
136 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing' --invert-regex" ); 136 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing' --invert-regex" );
137 cmp_ok( $res->return_code, "==", 0, "And also when not found"); 137 cmp_ok( $res->return_code, "==", 0, "And also when not found");
@@ -151,63 +151,74 @@ SKIP: {
151 151
152 $res = NPTest->testCmd( "./$plugin -C 8000,1 --ssl $host_tls_http" ); 152 $res = NPTest->testCmd( "./$plugin -C 8000,1 --ssl $host_tls_http" );
153 cmp_ok( $res->return_code, '==', 1, "Checking certificate for $host_tls_http"); 153 cmp_ok( $res->return_code, '==', 1, "Checking certificate for $host_tls_http");
154 like ( $res->output, qr/WARNING - Certificate '$host_tls_cert' expires in \d+ day/, "Output Warning" ); 154 like ( $res->output, qr/Certificate '$host_tls_cert' expires in \d+ day/, "Output Warning" );
155 155
156 $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" ); 156 $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" );
157 is( $res->return_code, 0, "Old syntax for cert checking okay" ); 157 is( $res->return_code, 0, "Old syntax for cert checking okay" );
158 is( $res->output, $saved_cert_output, "Same output as new syntax" ); 158 # deactivated since different timings will change the output
159 # TODO compare without perfdata
160 # is( $res->output, $saved_cert_output, "Same output as new syntax" );
159 161
160 $res = NPTest->testCmd( "./$plugin -H $host_tls_http -C 1" ); 162 $res = NPTest->testCmd( "./$plugin -H $host_tls_http -C 1" );
161 is( $res->return_code, 0, "Updated syntax for cert checking okay" ); 163 is( $res->return_code, 0, "Updated syntax for cert checking okay" );
162 is( $res->output, $saved_cert_output, "Same output as new syntax" ); 164 # deactivated since different timings will change the output
165 # TODO compare without perfdata
166 # is( $res->output, $saved_cert_output, "Same output as new syntax" );
163 167
164 $res = NPTest->testCmd( "./$plugin -C 1 $host_tls_http" ); 168 $res = NPTest->testCmd( "./$plugin -C 1 $host_tls_http" );
165 cmp_ok( $res->output, 'eq', $saved_cert_output, "--ssl option automatically added"); 169 # deactivated since different timings will change the output
170 # TODO compare without perfdata
171 # cmp_ok( $res->output, 'eq', $saved_cert_output, "--ssl option automatically added");
166 172
167 $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" ); 173 $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" );
168 cmp_ok( $res->output, 'eq', $saved_cert_output, "Old syntax for cert checking still works"); 174 # deactivated since different timings will change the output
175 # TODO compare without perfdata
176 # cmp_ok( $res->output, 'eq', $saved_cert_output, "Old syntax for cert checking still works");
169 177
170 # run some certificate checks with faketime 178 # run some certificate checks with faketime
171 SKIP: { 179 SKIP: {
172 skip "No faketime binary found", 12 if !$faketime; 180 skip "No faketime binary found", 12 if !$faketime;
173 $res = NPTest->testCmd("LC_TIME=C TZ=UTC ./$plugin -C 1 $host_tls_http"); 181 $res = NPTest->testCmd("LC_TIME=C TZ=UTC ./$plugin -C 1 $host_tls_http");
174 like($res->output, qr/OK - Certificate '$host_tls_cert' will expire on/, "Catch cert output"); 182 like($res->output, qr/Certificate '$host_tls_cert' will expire on/, "Catch cert output");
175 is( $res->return_code, 0, "Catch cert output exit code" ); 183 is( $res->return_code, 0, "Catch cert output exit code" );
184
176 my($mon,$day,$hour,$min,$sec,$year) = ($res->output =~ /(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)/); 185 my($mon,$day,$hour,$min,$sec,$year) = ($res->output =~ /(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)/);
177 if(!defined $year) { 186 if(!defined $year) {
178 die("parsing date failed from: ".$res->output); 187 die("parsing date failed from: ".$res->output);
179 } 188 }
189
180 my $months = {'Jan' => 0, 'Feb' => 1, 'Mar' => 2, 'Apr' => 3, 'May' => 4, 'Jun' => 5, 'Jul' => 6, 'Aug' => 7, 'Sep' => 8, 'Oct' => 9, 'Nov' => 10, 'Dec' => 11}; 190 my $months = {'Jan' => 0, 'Feb' => 1, 'Mar' => 2, 'Apr' => 3, 'May' => 4, 'Jun' => 5, 'Jul' => 6, 'Aug' => 7, 'Sep' => 8, 'Oct' => 9, 'Nov' => 10, 'Dec' => 11};
181 my $ts = mktime($sec, $min, $hour, $day, $months->{$mon}, $year-1900); 191 my $ts = mktime($sec, $min, $hour, $day, $months->{$mon}, $year-1900);
182 my $time = strftime("%Y-%m-%d %H:%M:%S", localtime($ts)); 192 my $time = strftime("%Y-%m-%d %H:%M:%S", localtime($ts));
193
183 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./$plugin -C 1 $host_tls_http"); 194 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./$plugin -C 1 $host_tls_http");
184 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' just expired/, "Output on expire date"); 195 like($res->output, qr/Certificate '$host_tls_cert' just expired/, "Output on expire date");
185 is( $res->return_code, 2, "Output on expire date" ); 196 is( $res->return_code, 2, "Output on expire date" );
186 197
187 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-1))."' ./$plugin -C 1 $host_tls_http"); 198 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-1))."' ./$plugin -C 1 $host_tls_http");
188 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 0 minutes/, "cert expires in 1 second output"); 199 like($res->output, qr/Certificate '$host_tls_cert' expires in 0 minutes/, "cert expires in 1 second output");
189 is( $res->return_code, 2, "cert expires in 1 second exit code" ); 200 is( $res->return_code, 2, "cert expires in 1 second exit code" );
190 201
191 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-120))."' ./$plugin -C 1 $host_tls_http"); 202 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-120))."' ./$plugin -C 1 $host_tls_http");
192 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 minutes/, "cert expires in 2 minutes output"); 203 like($res->output, qr/Certificate '$host_tls_cert' expires in 2 minutes/, "cert expires in 2 minutes output");
193 is( $res->return_code, 2, "cert expires in 2 minutes exit code" ); 204 is( $res->return_code, 2, "cert expires in 2 minutes exit code" );
194 205
195 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-7200))."' ./$plugin -C 1 $host_tls_http"); 206 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-7200))."' ./$plugin -C 1 $host_tls_http");
196 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 hours/, "cert expires in 2 hours output"); 207 like($res->output, qr/Certificate '$host_tls_cert' expires in 2 hours/, "cert expires in 2 hours output");
197 is( $res->return_code, 2, "cert expires in 2 hours exit code" ); 208 is( $res->return_code, 2, "cert expires in 2 hours exit code" );
198 209
199 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./$plugin -C 1 $host_tls_http"); 210 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./$plugin -C 1 $host_tls_http");
200 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expired on/, "Certificate expired output"); 211 like($res->output, qr/Certificate '$host_tls_cert' expired on/, "Certificate expired output");
201 is( $res->return_code, 2, "Certificate expired exit code" ); 212 is( $res->return_code, 2, "Certificate expired exit code" );
202 }; 213 };
203 214
204 $res = NPTest->testCmd( "./$plugin --ssl $host_tls_http -E" ); 215 $res = NPTest->testCmd( "./$plugin --ssl $host_tls_http -E" );
205 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' ); 216 like ( $res->output, '/\'time_connect\'=[\d\.]+/', 'Extended Performance Data Output OK' );
206 like ( $res->output, '/time_ssl=[\d\.]+/', 'Extended Performance Data SSL Output OK' ); 217 like ( $res->output, '/\'time_tls\'=[\d\.]+/', 'Extended Performance Data SSL Output OK' );
207 218
208 $res = NPTest->testCmd( "./$plugin -H monitoring-plugins.org -u /download.html -f follow" ); 219 $res = NPTest->testCmd( "./$plugin -H monitoring-plugins.org -u /download.html -f follow" );
209 is( $res->return_code, 0, "Redirection based on location is okay"); 220 is( $res->return_code, 0, "Redirection based on location is okay");
210 221
211 $res = NPTest->testCmd( "./$plugin -H monitoring-plugins.org --extended-perfdata" ); 222 $res = NPTest->testCmd( "./$plugin -H monitoring-plugins.org --extended-perfdata" );
212 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' ); 223 like ( $res->output, '/\'time_connect\'=[\d\.]+/', 'Extended Performance Data Output OK' );
213} 224}
diff --git a/plugins/tests/check_curl.t b/plugins/tests/check_curl.t
index eaa9f518..52c5ad1c 100755
--- a/plugins/tests/check_curl.t
+++ b/plugins/tests/check_curl.t
@@ -15,6 +15,7 @@
15# Email Address []:devel@monitoring-plugins.org 15# Email Address []:devel@monitoring-plugins.org
16 16
17use strict; 17use strict;
18use warnings;
18use Test::More; 19use Test::More;
19use NPTest; 20use NPTest;
20use FindBin qw($Bin); 21use FindBin qw($Bin);
@@ -245,21 +246,21 @@ SKIP: {
245 246
246 $result = NPTest->testCmd( "$command -p $port_https -S -C 14" ); 247 $result = NPTest->testCmd( "$command -p $port_https -S -C 14" );
247 is( $result->return_code, 0, "$command -p $port_https -S -C 14" ); 248 is( $result->return_code, 0, "$command -p $port_https -S -C 14" );
248 is( $result->output, "OK - Certificate 'Monitoring Plugins' will expire on $expiry.", "output ok" ); 249 like( $result->output, '/.*Certificate \'Monitoring Plugins\' will expire on ' . quotemeta($expiry) . '.*/', "output ok" );
249 250
250 $result = NPTest->testCmd( "$command -p $port_https -S -C 14000" ); 251 $result = NPTest->testCmd( "$command -p $port_https -S -C 14000" );
251 is( $result->return_code, 1, "$command -p $port_https -S -C 14000" ); 252 is( $result->return_code, 1, "$command -p $port_https -S -C 14000" );
252 like( $result->output, '/WARNING - Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\)./', "output ok" ); 253 like( $result->output, '/.*Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\).*/', "output ok" );
253 254
254 # Expired cert tests 255 # Expired cert tests
255 $result = NPTest->testCmd( "$command -p $port_https -S -C 13960,14000" ); 256 $result = NPTest->testCmd( "$command -p $port_https -S -C 13960,14000" );
256 is( $result->return_code, 2, "$command -p $port_https -S -C 13960,14000" ); 257 is( $result->return_code, 2, "$command -p $port_https -S -C 13960,14000" );
257 like( $result->output, '/CRITICAL - Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\)./', "output ok" ); 258 like( $result->output, '/.*Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\).*/', "output ok" );
258 259
259 $result = NPTest->testCmd( "$command -p $port_https_expired -S -C 7" ); 260 $result = NPTest->testCmd( "$command -p $port_https_expired -S -C 7" );
260 is( $result->return_code, 2, "$command -p $port_https_expired -S -C 7" ); 261 is( $result->return_code, 2, "$command -p $port_https_expired -S -C 7" );
261 is( $result->output, 262 like( $result->output,
262 'CRITICAL - Certificate \'Monitoring Plugins\' expired on Wed Jan 2 12:00:00 2008 +0000.', 263 '/.*Certificate \'Monitoring Plugins\' expired on Wed Jan\s+2 12:00:00 2008 \+0000.*/',
263 "output ok" ); 264 "output ok" );
264 265
265} 266}
@@ -274,19 +275,19 @@ SKIP: {
274 $cmd = "./$plugin -H $virtual_host -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host:$port_http\$"; 275 $cmd = "./$plugin -H $virtual_host -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host:$port_http\$";
275 $result = NPTest->testCmd( $cmd ); 276 $result = NPTest->testCmd( $cmd );
276 is( $result->return_code, 0, $cmd); 277 is( $result->return_code, 0, $cmd);
277 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 278 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
278 279
279 # http with virtual port (!= 80) 280 # http with virtual port (!= 80)
280 $cmd = "./$plugin -H $virtual_host:$virtual_port -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host:$virtual_port\$"; 281 $cmd = "./$plugin -H $virtual_host:$virtual_port -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host:$virtual_port\$";
281 $result = NPTest->testCmd( $cmd ); 282 $result = NPTest->testCmd( $cmd );
282 is( $result->return_code, 0, $cmd); 283 is( $result->return_code, 0, $cmd);
283 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 284 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
284 285
285 # http with virtual port (80) 286 # http with virtual port (80)
286 $cmd = "./$plugin -H $virtual_host:80 -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host\$"; 287 $cmd = "./$plugin -H $virtual_host:80 -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host\$";
287 $result = NPTest->testCmd( $cmd ); 288 $result = NPTest->testCmd( $cmd );
288 is( $result->return_code, 0, $cmd); 289 is( $result->return_code, 0, $cmd);
289 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 290 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
290} 291}
291 292
292# and the same for SSL 293# and the same for SSL
@@ -296,19 +297,19 @@ SKIP: {
296 $cmd = "./$plugin -H $virtual_host -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host:$port_https\$"; 297 $cmd = "./$plugin -H $virtual_host -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host:$port_https\$";
297 $result = NPTest->testCmd( $cmd ); 298 $result = NPTest->testCmd( $cmd );
298 is( $result->return_code, 0, $cmd); 299 is( $result->return_code, 0, $cmd);
299 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 300 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
300 301
301 # https with virtual port (!= 443) 302 # https with virtual port (!= 443)
302 $cmd = "./$plugin -H $virtual_host:$virtual_port -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host:$virtual_port\$"; 303 $cmd = "./$plugin -H $virtual_host:$virtual_port -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host:$virtual_port\$";
303 $result = NPTest->testCmd( $cmd ); 304 $result = NPTest->testCmd( $cmd );
304 is( $result->return_code, 0, $cmd); 305 is( $result->return_code, 0, $cmd);
305 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 306 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
306 307
307 # https with virtual port (443) 308 # https with virtual port (443)
308 $cmd = "./$plugin -H $virtual_host:443 -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host\$"; 309 $cmd = "./$plugin -H $virtual_host:443 -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host\$";
309 $result = NPTest->testCmd( $cmd ); 310 $result = NPTest->testCmd( $cmd );
310 is( $result->return_code, 0, $cmd); 311 is( $result->return_code, 0, $cmd);
311 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 312 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
312} 313}
313 314
314 315
@@ -321,165 +322,165 @@ sub run_common_tests {
321 322
322 $result = NPTest->testCmd( "$command -u /file/root" ); 323 $result = NPTest->testCmd( "$command -u /file/root" );
323 is( $result->return_code, 0, "/file/root"); 324 is( $result->return_code, 0, "/file/root");
324 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second/', "Output correct" ); 325 like( $result->output, '/.*HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second.*/', "Output correct" );
325 326
326 $result = NPTest->testCmd( "$command -u /file/root -s Root" ); 327 $result = NPTest->testCmd( "$command -u /file/root -s Root" );
327 is( $result->return_code, 0, "/file/root search for string"); 328 is( $result->return_code, 0, "/file/root search for string");
328 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second/', "Output correct" ); 329 like( $result->output, '/.*HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second.*/', "Output correct" );
329 330
330 $result = NPTest->testCmd( "$command -u /file/root -s NonRoot" ); 331 $result = NPTest->testCmd( "$command -u /file/root -s NonRoot" );
331 is( $result->return_code, 2, "Missing string check"); 332 is( $result->return_code, 2, "Missing string check");
332 like( $result->output, qr%^HTTP CRITICAL: HTTP/1\.1 200 OK - string 'NonRoot' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location"); 333 like( $result->output, qr%string 'NonRoot' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location");
333 334
334 $result = NPTest->testCmd( "$command -u /file/root -s NonRootWithOver30charsAndMoreFunThanAWetFish" ); 335 $result = NPTest->testCmd( "$command -u /file/root -s NonRootWithOver30charsAndMoreFunThanAWetFish" );
335 is( $result->return_code, 2, "Missing string check"); 336 is( $result->return_code, 2, "Missing string check");
336 like( $result->output, qr%HTTP CRITICAL: HTTP/1\.1 200 OK - string 'NonRootWithOver30charsAndM...' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location"); 337 like( $result->output, qr%string 'NonRootWithOver30charsAndM...' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location");
337 338
338 $result = NPTest->testCmd( "$command -u /header_check -d foo" ); 339 $result = NPTest->testCmd( "$command -u /header_check -d foo" );
339 is( $result->return_code, 0, "header_check search for string"); 340 is( $result->return_code, 0, "header_check search for string");
340 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 96 bytes in [\d\.]+ second/', "Output correct" ); 341 like( $result->output, '/.*HTTP/1.1 200 OK - 96 bytes in [\d\.]+ second.*/', "Output correct" );
341 342
342 $result = NPTest->testCmd( "$command -u /header_check -d bar" ); 343 $result = NPTest->testCmd( "$command -u /header_check -d bar" );
343 is( $result->return_code, 2, "Missing header string check"); 344 is( $result->return_code, 2, "Missing header string check");
344 like( $result->output, qr%^HTTP CRITICAL: HTTP/1\.1 200 OK - header 'bar' not found on 'https?://127\.0\.0\.1:\d+/header_check'%, "Shows search string and location"); 345 like( $result->output, qr%header 'bar' not found on 'https?://127\.0\.0\.1:\d+/header_check'%, "Shows search string and location");
345 346
346 $result = NPTest->testCmd( "$command -u /header_broken_check" ); 347 $result = NPTest->testCmd( "$command -u /header_broken_check" );
347 is( $result->return_code, 0, "header_check search for string"); 348 is( $result->return_code, 0, "header_check search for string");
348 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 138 bytes in [\d\.]+ second/', "Output correct" ); 349 like( $result->output, '/.*HTTP/1.1 200 OK - 138 bytes in [\d\.]+ second.*/', "Output correct" );
349 350
350 my $cmd; 351 my $cmd;
351 $cmd = "$command -u /slow"; 352 $cmd = "$command -u /slow";
352 $result = NPTest->testCmd( $cmd ); 353 $result = NPTest->testCmd( $cmd );
353 is( $result->return_code, 0, "$cmd"); 354 is( $result->return_code, 0, "$cmd");
354 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 355 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
355 $result->output =~ /in ([\d\.]+) second/; 356 $result->output =~ /in ([\d\.]+) second/;
356 cmp_ok( $1, ">", 1, "Time is > 1 second" ); 357 cmp_ok( $1, ">", 1, "Time is > 1 second" );
357 358
358 $cmd = "$command -u /statuscode/200"; 359 $cmd = "$command -u /statuscode/200";
359 $result = NPTest->testCmd( $cmd ); 360 $result = NPTest->testCmd( $cmd );
360 is( $result->return_code, 0, $cmd); 361 is( $result->return_code, 0, $cmd);
361 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 362 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
362 363
363 $cmd = "$command -u /statuscode/200 -e 200"; 364 $cmd = "$command -u /statuscode/200 -e 200";
364 $result = NPTest->testCmd( $cmd ); 365 $result = NPTest->testCmd( $cmd );
365 is( $result->return_code, 0, $cmd); 366 is( $result->return_code, 0, $cmd);
366 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - Status line output matched "200" - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 367 like( $result->output, '/.*Status line output matched "200".*/', "Output correct: ".$result->output );
367 368
368 $cmd = "$command -u /statuscode/201"; 369 $cmd = "$command -u /statuscode/201";
369 $result = NPTest->testCmd( $cmd ); 370 $result = NPTest->testCmd( $cmd );
370 is( $result->return_code, 0, $cmd); 371 is( $result->return_code, 0, $cmd);
371 like( $result->output, '/^HTTP OK: HTTP/1.1 201 Created - \d+ bytes in [\d\.]+ second /', "Output correct: ".$result->output ); 372 like( $result->output, '/.*HTTP/1.1 201 Created - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
372 373
373 $cmd = "$command -u /statuscode/201 -e 201"; 374 $cmd = "$command -u /statuscode/201 -e 201";
374 $result = NPTest->testCmd( $cmd ); 375 $result = NPTest->testCmd( $cmd );
375 is( $result->return_code, 0, $cmd); 376 is( $result->return_code, 0, $cmd);
376 like( $result->output, '/^HTTP OK: HTTP/1.1 201 Created - Status line output matched "201" - \d+ bytes in [\d\.]+ second /', "Output correct: ".$result->output ); 377 like( $result->output, '/.*Status line output matched "201".*/', "Output correct: ".$result->output );
377 378
378 $cmd = "$command -u /statuscode/201 -e 200"; 379 $cmd = "$command -u /statuscode/201 -e 200";
379 $result = NPTest->testCmd( $cmd ); 380 $result = NPTest->testCmd( $cmd );
380 is( $result->return_code, 2, $cmd); 381 is( $result->return_code, 2, $cmd);
381 like( $result->output, '/^HTTP CRITICAL - Invalid HTTP response received from host on port \d+: HTTP/1.1 201 Created/', "Output correct: ".$result->output ); 382 like( $result->output, '/.*Invalid HTTP response received from host on port \d+: HTTP/1.1 201 Created.*/', "Output correct: ".$result->output );
382 383
383 $cmd = "$command -u /statuscode/200 -e 200,201,202"; 384 $cmd = "$command -u /statuscode/200 -e 200,201,202";
384 $result = NPTest->testCmd( $cmd ); 385 $result = NPTest->testCmd( $cmd );
385 is( $result->return_code, 0, $cmd); 386 is( $result->return_code, 0, $cmd);
386 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - Status line output matched "200,201,202" - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 387 like( $result->output, '/.*Status line output matched "200,201,202".*/', "Output correct: ".$result->output );
387 388
388 $cmd = "$command -u /statuscode/201 -e 200,201,202"; 389 $cmd = "$command -u /statuscode/201 -e 200,201,202";
389 $result = NPTest->testCmd( $cmd ); 390 $result = NPTest->testCmd( $cmd );
390 is( $result->return_code, 0, $cmd); 391 is( $result->return_code, 0, $cmd);
391 like( $result->output, '/^HTTP OK: HTTP/1.1 201 Created - Status line output matched "200,201,202" - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 392 like( $result->output, '/.*Status line output matched "200,201,202".*/', "Output correct: ".$result->output );
392 393
393 $cmd = "$command -u /statuscode/203 -e 200,201,202"; 394 $cmd = "$command -u /statuscode/203 -e 200,201,202";
394 $result = NPTest->testCmd( $cmd ); 395 $result = NPTest->testCmd( $cmd );
395 is( $result->return_code, 2, $cmd); 396 is( $result->return_code, 2, $cmd);
396 like( $result->output, '/^HTTP CRITICAL - Invalid HTTP response received from host on port (\d+): HTTP/1.1 203 Non-Authoritative Information/', "Output correct: ".$result->output ); 397 like( $result->output, '/.*Invalid HTTP response received from host on port (\d+): HTTP/1.1 203 Non-Authoritative Information.*/', "Output correct: ".$result->output );
397 398
398 $cmd = "$command -j HEAD -u /method"; 399 $cmd = "$command -j HEAD -u /method";
399 $result = NPTest->testCmd( $cmd ); 400 $result = NPTest->testCmd( $cmd );
400 is( $result->return_code, 0, $cmd); 401 is( $result->return_code, 0, $cmd);
401 like( $result->output, '/^HTTP OK: HTTP/1.1 200 HEAD - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 402 like( $result->output, '/.*HTTP/1.1 200 HEAD - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
402 403
403 $cmd = "$command -j POST -u /method"; 404 $cmd = "$command -j POST -u /method";
404 $result = NPTest->testCmd( $cmd ); 405 $result = NPTest->testCmd( $cmd );
405 is( $result->return_code, 0, $cmd); 406 is( $result->return_code, 0, $cmd);
406 like( $result->output, '/^HTTP OK: HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 407 like( $result->output, '/.*HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
407 408
408 $cmd = "$command -j GET -u /method"; 409 $cmd = "$command -j GET -u /method";
409 $result = NPTest->testCmd( $cmd ); 410 $result = NPTest->testCmd( $cmd );
410 is( $result->return_code, 0, $cmd); 411 is( $result->return_code, 0, $cmd);
411 like( $result->output, '/^HTTP OK: HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 412 like( $result->output, '/.*HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
412 413
413 $cmd = "$command -u /method"; 414 $cmd = "$command -u /method";
414 $result = NPTest->testCmd( $cmd ); 415 $result = NPTest->testCmd( $cmd );
415 is( $result->return_code, 0, $cmd); 416 is( $result->return_code, 0, $cmd);
416 like( $result->output, '/^HTTP OK: HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 417 like( $result->output, '/.*HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
417 418
418 $cmd = "$command -P foo -u /method"; 419 $cmd = "$command -P foo -u /method";
419 $result = NPTest->testCmd( $cmd ); 420 $result = NPTest->testCmd( $cmd );
420 is( $result->return_code, 0, $cmd); 421 is( $result->return_code, 0, $cmd);
421 like( $result->output, '/^HTTP OK: HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 422 like( $result->output, '/.*HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
422 423
423 $cmd = "$command -j DELETE -u /method"; 424 $cmd = "$command -j DELETE -u /method";
424 $result = NPTest->testCmd( $cmd ); 425 $result = NPTest->testCmd( $cmd );
425 is( $result->return_code, 1, $cmd); 426 is( $result->return_code, 1, $cmd);
426 like( $result->output, '/^HTTP WARNING: HTTP/1.1 405 Method Not Allowed/', "Output correct: ".$result->output ); 427 like( $result->output, '/.*HTTP/1.1 405 Method Not Allowed.*/', "Output correct: ".$result->output );
427 428
428 $cmd = "$command -j foo -u /method"; 429 $cmd = "$command -j foo -u /method";
429 $result = NPTest->testCmd( $cmd ); 430 $result = NPTest->testCmd( $cmd );
430 is( $result->return_code, 2, $cmd); 431 is( $result->return_code, 2, $cmd);
431 like( $result->output, '/^HTTP CRITICAL: HTTP/1.1 501 Not Implemented/', "Output correct: ".$result->output ); 432 like( $result->output, '/.*HTTP/1.1 501 Not Implemented.*/', "Output correct: ".$result->output );
432 433
433 $cmd = "$command -P stufftoinclude -u /postdata -s POST:stufftoinclude"; 434 $cmd = "$command -P stufftoinclude -u /postdata -s POST:stufftoinclude";
434 $result = NPTest->testCmd( $cmd ); 435 $result = NPTest->testCmd( $cmd );
435 is( $result->return_code, 0, $cmd); 436 is( $result->return_code, 0, $cmd);
436 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 437 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
437 438
438 $cmd = "$command -j PUT -P stufftoinclude -u /postdata -s PUT:stufftoinclude"; 439 $cmd = "$command -j PUT -P stufftoinclude -u /postdata -s PUT:stufftoinclude";
439 $result = NPTest->testCmd( $cmd ); 440 $result = NPTest->testCmd( $cmd );
440 is( $result->return_code, 0, $cmd); 441 is( $result->return_code, 0, $cmd);
441 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 442 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
442 443
443 # To confirm that the free doesn't segfault 444 # To confirm that the free doesn't segfault
444 $cmd = "$command -P stufftoinclude -j PUT -u /postdata -s PUT:stufftoinclude"; 445 $cmd = "$command -P stufftoinclude -j PUT -u /postdata -s PUT:stufftoinclude";
445 $result = NPTest->testCmd( $cmd ); 446 $result = NPTest->testCmd( $cmd );
446 is( $result->return_code, 0, $cmd); 447 is( $result->return_code, 0, $cmd);
447 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 448 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
448 449
449 $cmd = "$command -u /redirect"; 450 $cmd = "$command -u /redirect";
450 $result = NPTest->testCmd( $cmd ); 451 $result = NPTest->testCmd( $cmd );
451 is( $result->return_code, 0, $cmd); 452 is( $result->return_code, 0, $cmd);
452 like( $result->output, '/^HTTP OK: HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 453 like( $result->output, '/.*HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
453 454
454 $cmd = "$command -f follow -u /redirect"; 455 $cmd = "$command -f follow -u /redirect";
455 $result = NPTest->testCmd( $cmd ); 456 $result = NPTest->testCmd( $cmd );
456 is( $result->return_code, 0, $cmd); 457 is( $result->return_code, 0, $cmd);
457 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 458 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
458 459
459 $cmd = "$command -u /redirect -k 'follow: me'"; 460 $cmd = "$command -u /redirect -k 'follow: me'";
460 $result = NPTest->testCmd( $cmd ); 461 $result = NPTest->testCmd( $cmd );
461 is( $result->return_code, 0, $cmd); 462 is( $result->return_code, 0, $cmd);
462 like( $result->output, '/^HTTP OK: HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 463 like( $result->output, '/.*HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
463 464
464 $cmd = "$command -f follow -u /redirect -k 'follow: me'"; 465 $cmd = "$command -f follow -u /redirect -k 'follow: me'";
465 $result = NPTest->testCmd( $cmd ); 466 $result = NPTest->testCmd( $cmd );
466 is( $result->return_code, 0, $cmd); 467 is( $result->return_code, 0, $cmd);
467 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 468 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
468 469
469 $cmd = "$command -f sticky -u /redirect -k 'follow: me'"; 470 $cmd = "$command -f sticky -u /redirect -k 'follow: me'";
470 $result = NPTest->testCmd( $cmd ); 471 $result = NPTest->testCmd( $cmd );
471 is( $result->return_code, 0, $cmd); 472 is( $result->return_code, 0, $cmd);
472 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 473 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
473 474
474 $cmd = "$command -f stickyport -u /redirect -k 'follow: me'"; 475 $cmd = "$command -f stickyport -u /redirect -k 'follow: me'";
475 $result = NPTest->testCmd( $cmd ); 476 $result = NPTest->testCmd( $cmd );
476 is( $result->return_code, 0, $cmd); 477 is( $result->return_code, 0, $cmd);
477 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 478 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
478 479
479 $cmd = "$command -f follow -u /redirect_rel -s redirected"; 480 $cmd = "$command -f follow -u /redirect_rel -s redirected";
480 $result = NPTest->testCmd( $cmd ); 481 $result = NPTest->testCmd( $cmd );
481 is( $result->return_code, 0, $cmd); 482 is( $result->return_code, 0, $cmd);
482 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); 483 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
483 484
484 # These tests may block 485 # These tests may block
485 # stickyport - on full urlS port is set back to 80 otherwise 486 # stickyport - on full urlS port is set back to 80 otherwise