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