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