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