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