diff options
| author | Andreas Baumann <mail@andreasbaumann.cc> | 2017-01-19 10:23:00 +0100 |
|---|---|---|
| committer | Sven Nierlein <sven@nierlein.de> | 2018-10-22 16:28:51 +0200 |
| commit | 2a9812ee43d64d11be96544f84501686541da86b (patch) | |
| tree | d302ea1127a09f31a9ba82915b6ca7b53da2aab0 | |
| parent | 0b85260bd2d6b2fd62588c71daf44bc1156a632f (diff) | |
| download | monitoring-plugins-2a9812ee43d64d11be96544f84501686541da86b.tar.gz | |
added most options from nagios-curl-plugin
| -rw-r--r-- | plugins/check_curl.c | 658 |
1 files changed, 640 insertions, 18 deletions
diff --git a/plugins/check_curl.c b/plugins/check_curl.c index be024feb..e4230dc4 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c | |||
| @@ -40,6 +40,20 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 40 | #include "common.h" | 40 | #include "common.h" |
| 41 | #include "utils.h" | 41 | #include "utils.h" |
| 42 | 42 | ||
| 43 | #ifdef HAVE_SYS_TYPES_H | ||
| 44 | #include <sys/types.h> | ||
| 45 | #else | ||
| 46 | #define unsigned int size_t | ||
| 47 | #endif | ||
| 48 | |||
| 49 | #ifdef HAVE_STRING_H | ||
| 50 | #include <string.h> | ||
| 51 | #endif | ||
| 52 | |||
| 53 | #ifdef HAVE_STDLIB_H | ||
| 54 | #include <stdlib.h> | ||
| 55 | #endif | ||
| 56 | |||
| 43 | #ifndef LIBCURL_PROTOCOL_HTTP | 57 | #ifndef LIBCURL_PROTOCOL_HTTP |
| 44 | #error libcurl compiled without HTTP support, compiling check_curl plugin makes not much sense | 58 | #error libcurl compiled without HTTP support, compiling check_curl plugin makes not much sense |
| 45 | #endif | 59 | #endif |
| @@ -47,50 +61,343 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 47 | #include "curl/curl.h" | 61 | #include "curl/curl.h" |
| 48 | #include "curl/easy.h" | 62 | #include "curl/easy.h" |
| 49 | 63 | ||
| 50 | int verbose = FALSE; | 64 | #define DEFAULT_BUFFER_SIZE 2048 |
| 65 | #define DEFAULT_SERVER_URL "/" | ||
| 66 | #define DEFAULT_HTTP_PORT 80 | ||
| 67 | #define DEFAULT_HTTPS_PORT 443 | ||
| 68 | #define MAX_PORT 65535 | ||
| 69 | |||
| 70 | /* for buffers for header and body */ | ||
| 71 | typedef struct { | ||
| 72 | char *buf; | ||
| 73 | size_t buflen; | ||
| 74 | size_t bufsize; | ||
| 75 | } curlhelp_curlbuf; | ||
| 76 | |||
| 77 | /* for parsing the HTTP status line */ | ||
| 78 | typedef struct { | ||
| 79 | int http_major; /* major version of the protocol, always 1 (HTTP/0.9 | ||
| 80 | * never reached the big internet most likely) */ | ||
| 81 | int http_minor; /* minor version of the protocol, usually 0 or 1 */ | ||
| 82 | int http_code; /* HTTP return code as in RFC 2145 */ | ||
| 83 | int http_subcode; /* Microsoft IIS extension, HTTP subcodes, see | ||
| 84 | * http://support.microsoft.com/kb/318380/en-us */ | ||
| 85 | char *msg; /* the human readable message */ | ||
| 86 | char *first_line; /* a copy of the first line */ | ||
| 87 | } curlhelp_statusline; | ||
| 88 | |||
| 89 | char *server_address; | ||
| 90 | char *host_name; | ||
| 91 | char *server_url = DEFAULT_SERVER_URL; | ||
| 92 | unsigned short server_port = DEFAULT_HTTP_PORT; | ||
| 93 | char *warning_thresholds = NULL; | ||
| 94 | char *critical_thresholds = NULL; | ||
| 95 | thresholds *thlds; | ||
| 96 | char user_agent[DEFAULT_BUFFER_SIZE]; | ||
| 97 | int verbose = 0; | ||
| 51 | CURL *curl; | 98 | CURL *curl; |
| 99 | struct curl_slist *header_list = NULL; | ||
| 100 | curlhelp_curlbuf body_buf; | ||
| 101 | curlhelp_curlbuf header_buf; | ||
| 102 | curlhelp_statusline status_line; | ||
| 103 | char http_header[DEFAULT_BUFFER_SIZE]; | ||
| 104 | long code; | ||
| 105 | long socket_timeout = DEFAULT_SOCKET_TIMEOUT; | ||
| 106 | double total_time; | ||
| 107 | char errbuf[CURL_ERROR_SIZE+1]; | ||
| 108 | CURLcode res; | ||
| 109 | char url[DEFAULT_BUFFER_SIZE]; | ||
| 110 | char msg[DEFAULT_BUFFER_SIZE]; | ||
| 111 | char perfstring[DEFAULT_BUFFER_SIZE]; | ||
| 112 | char user_auth[MAX_INPUT_BUFFER] = ""; | ||
| 113 | int onredirect = STATE_OK; | ||
| 114 | int use_ssl = FALSE; | ||
| 115 | int use_sni = TRUE; | ||
| 116 | int check_cert = FALSE; | ||
| 117 | int ssl_version = CURL_SSLVERSION_DEFAULT; | ||
| 118 | char *client_cert = NULL; | ||
| 119 | char *client_privkey = NULL; | ||
| 52 | 120 | ||
| 53 | int process_arguments (int, char **); | 121 | int process_arguments (int, char**); |
| 54 | void print_help (void); | 122 | void print_help (void); |
| 55 | void print_usage (void); | 123 | void print_usage (void); |
| 56 | void print_curl_version (void); | 124 | void print_curl_version (void); |
| 125 | int curlhelp_initbuffer (curlhelp_curlbuf*); | ||
| 126 | int curlhelp_buffer_callback (void*, size_t , size_t , void*); | ||
| 127 | void curlhelp_freebuffer (curlhelp_curlbuf*); | ||
| 128 | |||
| 129 | int curlhelp_parse_statusline (char*, curlhelp_statusline *); | ||
| 130 | void curlhelp_free_statusline (curlhelp_statusline *); | ||
| 131 | |||
| 132 | void remove_newlines (char *); | ||
| 133 | void test_file (char *); | ||
| 57 | 134 | ||
| 58 | int | 135 | int |
| 59 | main (int argc, char **argv) | 136 | main (int argc, char **argv) |
| 60 | { | 137 | { |
| 61 | int result = STATE_UNKNOWN; | 138 | int result = STATE_OK; |
| 62 | 139 | ||
| 63 | setlocale (LC_ALL, ""); | 140 | setlocale (LC_ALL, ""); |
| 64 | bindtextdomain (PACKAGE, LOCALEDIR); | 141 | bindtextdomain (PACKAGE, LOCALEDIR); |
| 65 | textdomain (PACKAGE); | 142 | textdomain (PACKAGE); |
| 66 | 143 | ||
| 67 | /* Parse extra opts if any */ | 144 | /* Parse extra opts if any */ |
| 68 | argv=np_extra_opts (&argc, argv, progname); | 145 | argv = np_extra_opts (&argc, argv, progname); |
| 146 | |||
| 147 | /* set defaults */ | ||
| 148 | snprintf( user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s)", | ||
| 149 | progname, NP_VERSION, VERSION); | ||
| 69 | 150 | ||
| 151 | /* parse arguments */ | ||
| 70 | if (process_arguments (argc, argv) == ERROR) | 152 | if (process_arguments (argc, argv) == ERROR) |
| 71 | usage4 (_("Could not parse arguments")); | 153 | usage4 (_("Could not parse arguments")); |
| 72 | 154 | ||
| 155 | /* initialize curl */ | ||
| 73 | if (curl_global_init (CURL_GLOBAL_DEFAULT) != CURLE_OK) | 156 | if (curl_global_init (CURL_GLOBAL_DEFAULT) != CURLE_OK) |
| 74 | die (STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n"); | 157 | die (STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n"); |
| 75 | 158 | ||
| 76 | if ((curl = curl_easy_init()) == NULL) | 159 | if ((curl = curl_easy_init()) == NULL) |
| 77 | die (STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n"); | 160 | die (STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n"); |
| 78 | 161 | ||
| 162 | if (verbose >= 3) | ||
| 163 | curl_easy_setopt (curl, CURLOPT_VERBOSE, TRUE); | ||
| 164 | |||
| 165 | /* initialize buffer for body of the answer */ | ||
| 166 | if (curlhelp_initbuffer(&body_buf) < 0) | ||
| 167 | die (STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n"); | ||
| 168 | curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, curlhelp_buffer_callback); | ||
| 169 | curl_easy_setopt (curl, CURLOPT_WRITEDATA, (void *)&body_buf); | ||
| 170 | |||
| 171 | /* initialize buffer for header of the answer */ | ||
| 172 | if (curlhelp_initbuffer( &header_buf ) < 0) | ||
| 173 | die (STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n" ); | ||
| 174 | curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, curlhelp_buffer_callback); | ||
| 175 | curl_easy_setopt (curl, CURLOPT_WRITEHEADER, (void *)&header_buf); | ||
| 176 | |||
| 177 | /* set the error buffer */ | ||
| 178 | curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, errbuf); | ||
| 179 | |||
| 180 | /* set timeouts */ | ||
| 181 | curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, socket_timeout); | ||
| 182 | curl_easy_setopt (curl, CURLOPT_TIMEOUT, socket_timeout); | ||
| 183 | |||
| 184 | /* compose URL */ | ||
| 185 | snprintf (url, DEFAULT_BUFFER_SIZE, "%s://%s%s", use_ssl ? "https" : "http", | ||
| 186 | server_address, server_url); | ||
| 187 | curl_easy_setopt (curl, CURLOPT_URL, url); | ||
| 188 | |||
| 189 | /* set port */ | ||
| 190 | curl_easy_setopt (curl, CURLOPT_PORT, server_port); | ||
| 191 | |||
| 192 | /* compose HTTP headers */ | ||
| 193 | snprintf (http_header, DEFAULT_BUFFER_SIZE, "Host: %s", host_name); | ||
| 194 | header_list = curl_slist_append (header_list, http_header); | ||
| 195 | curl_easy_setopt( curl, CURLOPT_HTTPHEADER, header_list ); | ||
| 196 | |||
| 197 | /* set SSL version, warn about unsecure or unsupported versions */ | ||
| 198 | if (use_ssl) { | ||
| 199 | curl_easy_setopt (curl, CURLOPT_SSLVERSION, ssl_version); | ||
| 200 | } | ||
| 201 | |||
| 202 | /* client certificate and key to present to server (SSL) */ | ||
| 203 | if (client_cert) | ||
| 204 | curl_easy_setopt (curl, CURLOPT_SSLCERT, client_cert); | ||
| 205 | if (client_privkey) | ||
| 206 | curl_easy_setopt (curl, CURLOPT_SSLKEY, client_privkey); | ||
| 207 | |||
| 208 | /* per default if we have a CA verify both the peer and the | ||
| 209 | * hostname in the certificate, can be switched off later */ | ||
| 210 | curl_easy_setopt( curl, CURLOPT_SSL_VERIFYPEER, 2); | ||
| 211 | curl_easy_setopt( curl, CURLOPT_SSL_VERIFYHOST, 2); | ||
| 212 | |||
| 213 | /* backward-compatible behaviour, be tolerant in checks */ | ||
| 214 | if (!check_cert) { | ||
| 215 | //TODO: depending on more options have aspects we want | ||
| 216 | //to be tolerant about | ||
| 217 | //curl_easy_setopt( curl, CURLOPT_SSL_VERIFYPEER, 1 ); | ||
| 218 | curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0); | ||
| 219 | curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0); | ||
| 220 | } | ||
| 221 | |||
| 222 | /* set default or user-given user agent identification */ | ||
| 223 | curl_easy_setopt (curl, CURLOPT_USERAGENT, user_agent); | ||
| 224 | |||
| 225 | /* authentication */ | ||
| 226 | if (strcmp(user_auth, "")) | ||
| 227 | curl_easy_setopt (curl, CURLOPT_USERPWD, user_auth); | ||
| 228 | |||
| 229 | /* TODO: parameter auth method, bitfield of following methods: | ||
| 230 | * CURLAUTH_BASIC (default) | ||
| 231 | * CURLAUTH_DIGEST | ||
| 232 | * CURLAUTH_DIGEST_IE | ||
| 233 | * CURLAUTH_NEGOTIATE | ||
| 234 | * CURLAUTH_NTLM | ||
| 235 | * CURLAUTH_NTLM_WB | ||
| 236 | * | ||
| 237 | * convenience tokens for typical sets of methods: | ||
| 238 | * CURLAUTH_ANYSAFE: most secure, without BASIC | ||
| 239 | * or CURLAUTH_ANY: most secure, even BASIC if necessary | ||
| 240 | * | ||
| 241 | * curl_easy_setopt( curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_DIGEST ); | ||
| 242 | */ | ||
| 243 | |||
| 244 | /* TODO: --cacert: CA certificate file to verify SSL connection against (SSL) */ | ||
| 245 | //~ if( args_info.cacert_given ) { | ||
| 246 | //~ curl_easy_setopt( curl, CURLOPT_CAINFO, args_info.cacert_arg ); | ||
| 247 | //~ } | ||
| 248 | |||
| 249 | /* TODO: old option -s: check if the excepted string matches */ | ||
| 250 | //~ if( args_info.string_given ) { | ||
| 251 | //~ if( strstr( body_buf.buf, args_info.string_arg ) == NULL ) { | ||
| 252 | //~ printf( "HTTP CRITICAL - string not found|%s\n", perfstring ); | ||
| 253 | //~ curl_easy_cleanup( curl ); | ||
| 254 | //~ curl_global_cleanup( ); | ||
| 255 | //~ curlhelp_freebuffer( &body_buf ); | ||
| 256 | //~ curlhelp_freebuffer( &header_buf ); | ||
| 257 | //~ exit( STATE_CRITICAL ); | ||
| 258 | //~ } | ||
| 259 | //~ } | ||
| 260 | |||
| 261 | /* handle redirections */ | ||
| 262 | if (onredirect == STATE_DEPENDENT) { | ||
| 263 | curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1); | ||
| 264 | /* TODO: handle the following aspects of redirection | ||
| 265 | CURLOPT_POSTREDIR: method switch | ||
| 266 | CURLINFO_REDIRECT_URL: custom redirect option | ||
| 267 | CURLOPT_REDIRECT_PROTOCOLS | ||
| 268 | CURLINFO_REDIRECT_COUNT | ||
| 269 | */ | ||
| 270 | } | ||
| 271 | |||
| 272 | /* do the request */ | ||
| 273 | res = curl_easy_perform(curl); | ||
| 274 | |||
| 275 | /* free header list, we don't need it anymore */ | ||
| 276 | curl_slist_free_all (header_list); | ||
| 277 | |||
| 278 | /* Curl errors, result in critical Nagios state */ | ||
| 279 | if (res != CURLE_OK) { | ||
| 280 | remove_newlines (errbuf); | ||
| 281 | snprintf (msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), | ||
| 282 | server_port, status_line.msg, status_line.msg); | ||
| 283 | die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", errbuf); | ||
| 284 | } | ||
| 285 | |||
| 286 | /* we got the data and we executed the request in a given time, so we can append | ||
| 287 | * performance data to the answer always | ||
| 288 | */ | ||
| 289 | curl_easy_getinfo (curl, CURLINFO_TOTAL_TIME, &total_time); | ||
| 290 | snprintf (perfstring, DEFAULT_BUFFER_SIZE, "time=%.6gs;%.6g;%.6g;%.6g size=%dB;;;0", | ||
| 291 | total_time, | ||
| 292 | 0.0, 0.0, | ||
| 293 | //~ args_info.warning_given ? args_info.warning_arg : 0.0, | ||
| 294 | //~ args_info.critical_given ? args_info.critical_arg : 0.0, | ||
| 295 | 0.0, | ||
| 296 | (int)body_buf.buflen); | ||
| 297 | |||
| 298 | /* return a CRITICAL status if we couldn't read any data */ | ||
| 299 | if (strlen(header_buf.buf) == 0 && strlen(body_buf.buf) == 0) | ||
| 300 | die (STATE_CRITICAL, _("HTTP CRITICAL - No header received from host\n")); | ||
| 301 | |||
| 302 | /* get status line of answer, check sanity of HTTP code */ | ||
| 303 | if (curlhelp_parse_statusline (header_buf.buf, &status_line) < 0) { | ||
| 304 | snprintf (msg, DEFAULT_BUFFER_SIZE, "Unparseable status line in %.3g seconds response time|%s\n", | ||
| 305 | code, total_time, perfstring); | ||
| 306 | die (STATE_CRITICAL, "HTTP CRITICAL HTTP/1.x %d unknown - %s", code, msg); | ||
| 307 | } | ||
| 308 | |||
| 309 | /* get result code from cURL */ | ||
| 310 | curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &code); | ||
| 311 | if (verbose>=2) | ||
| 312 | printf ("* curl CURLINFO_RESPONSE_CODE is %d\n", code); | ||
| 313 | |||
| 314 | /* print status line, header, body if verbose */ | ||
| 315 | if (verbose >= 2) { | ||
| 316 | puts ("--- HEADER ---"); | ||
| 317 | puts (header_buf.buf); | ||
| 318 | puts ("--- BODY ---"); | ||
| 319 | puts (body_buf.buf); | ||
| 320 | } | ||
| 321 | |||
| 322 | /* illegal return codes result in a critical state */ | ||
| 323 | if (code >= 600 || code < 100) { | ||
| 324 | die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), status_line.http_code, status_line.msg); | ||
| 325 | /* server errors result in a critical state */ | ||
| 326 | } else if (code >= 500) { | ||
| 327 | result = STATE_CRITICAL; | ||
| 328 | /* client errors result in a warning state */ | ||
| 329 | } else if (code >= 400) { | ||
| 330 | result = STATE_WARNING; | ||
| 331 | /* check redirected page if specified */ | ||
| 332 | } else if (code >= 300) { | ||
| 333 | if (onredirect == STATE_DEPENDENT) { | ||
| 334 | code = status_line.http_code; | ||
| 335 | } | ||
| 336 | result = max_state_alt (onredirect, result); | ||
| 337 | // TODO: make sure the last status line has been | ||
| 338 | // parsed into the status_line structure | ||
| 339 | /* all other codes are considered ok */ | ||
| 340 | } else { | ||
| 341 | result = STATE_OK; | ||
| 342 | } | ||
| 343 | |||
| 344 | /* check status codes, set exit status accordingly */ | ||
| 345 | if( status_line.http_code != code ) { | ||
| 346 | die (STATE_CRITICAL, _("HTTP CRITICAL HTTP/%d.%d %d %s - different HTTP codes (cUrl has %ld)\n"), | ||
| 347 | status_line.http_major, status_line.http_minor, | ||
| 348 | status_line.http_code, status_line.msg, code); | ||
| 349 | } | ||
| 350 | |||
| 351 | /* -w, -c: check warning and critical level */ | ||
| 352 | result = max_state_alt(get_status(total_time, thlds), result); | ||
| 353 | |||
| 354 | //~ die (result, "HTTP %s: %s\n", state_text(result), msg); | ||
| 355 | die (result, "HTTP %s HTTP/%d.%d %d %s - %.3g seconds response time|%s\n", | ||
| 356 | state_text(result), status_line.http_major, status_line.http_minor, | ||
| 357 | status_line.http_code, status_line.msg, | ||
| 358 | total_time, perfstring); | ||
| 359 | |||
| 360 | /* proper cleanup after die? */ | ||
| 361 | curlhelp_free_statusline(&status_line); | ||
| 79 | curl_easy_cleanup (curl); | 362 | curl_easy_cleanup (curl); |
| 80 | curl_global_cleanup (); | 363 | curl_global_cleanup (); |
| 81 | 364 | curlhelp_freebuffer(&body_buf); | |
| 365 | curlhelp_freebuffer(&header_buf); | ||
| 366 | |||
| 82 | return result; | 367 | return result; |
| 83 | } | 368 | } |
| 84 | 369 | ||
| 370 | /* check whether a file exists */ | ||
| 371 | void | ||
| 372 | test_file (char *path) | ||
| 373 | { | ||
| 374 | if (access(path, R_OK) == 0) | ||
| 375 | return; | ||
| 376 | usage2 (_("file does not exist or is not readable"), path); | ||
| 377 | } | ||
| 378 | |||
| 85 | int | 379 | int |
| 86 | process_arguments (int argc, char **argv) | 380 | process_arguments (int argc, char **argv) |
| 87 | { | 381 | { |
| 88 | int c; | 382 | int c; |
| 383 | |||
| 384 | enum { | ||
| 385 | SNI_OPTION | ||
| 386 | }; | ||
| 387 | |||
| 89 | int option=0; | 388 | int option=0; |
| 90 | static struct option longopts[] = { | 389 | static struct option longopts[] = { |
| 91 | {"version", no_argument, 0, 'V'}, | 390 | {"ssl", optional_argument, 0, 'S'}, |
| 92 | {"help", no_argument, 0, 'h'}, | 391 | {"sni", no_argument, 0, SNI_OPTION}, |
| 93 | {"verbose", no_argument, 0, 'v'}, | 392 | {"IP-address", required_argument, 0, 'I'}, |
| 393 | {"url", required_argument, 0, 'u'}, | ||
| 394 | {"port", required_argument, 0, 'p'}, | ||
| 395 | {"authorization", required_argument, 0, 'a'}, | ||
| 396 | {"onredirect", required_argument, 0, 'f'}, | ||
| 397 | {"client-cert", required_argument, 0, 'J'}, | ||
| 398 | {"private-key", required_argument, 0, 'K'}, | ||
| 399 | {"useragent", required_argument, 0, 'A'}, | ||
| 400 | {"certificate", required_argument, 0, 'C'}, | ||
| 94 | {0, 0, 0, 0} | 401 | {0, 0, 0, 0} |
| 95 | }; | 402 | }; |
| 96 | 403 | ||
| @@ -98,7 +405,7 @@ process_arguments (int argc, char **argv) | |||
| 98 | usage ("\n"); | 405 | usage ("\n"); |
| 99 | 406 | ||
| 100 | while (1) { | 407 | while (1) { |
| 101 | c = getopt_long (argc, argv, "Vhv", longopts, &option); | 408 | c = getopt_long (argc, argv, "Vvht:c:w:A:H:I:a:p:u:f:C:J:K:S::", longopts, &option); |
| 102 | if (c == -1 || c == EOF || c == 1) | 409 | if (c == -1 || c == EOF || c == 1) |
| 103 | break; | 410 | break; |
| 104 | 411 | ||
| @@ -115,14 +422,155 @@ process_arguments (int argc, char **argv) | |||
| 115 | case 'v': | 422 | case 'v': |
| 116 | verbose++; | 423 | verbose++; |
| 117 | break; | 424 | break; |
| 425 | case 't': /* timeout period */ | ||
| 426 | if (!is_intnonneg (optarg)) | ||
| 427 | usage2 (_("Timeout interval must be a positive integer"), optarg); | ||
| 428 | else | ||
| 429 | socket_timeout = (int)strtol (optarg, NULL, 10); | ||
| 430 | break; | ||
| 431 | case 'c': /* critical time threshold */ | ||
| 432 | critical_thresholds = optarg; | ||
| 433 | break; | ||
| 434 | case 'w': /* warning time threshold */ | ||
| 435 | warning_thresholds = optarg; | ||
| 436 | break; | ||
| 437 | case 'H': /* virtual host */ | ||
| 438 | host_name = strdup (optarg); | ||
| 439 | break; | ||
| 440 | case 'I': /* internet address */ | ||
| 441 | server_address = strdup (optarg); | ||
| 442 | break; | ||
| 443 | case 'u': /* URL path */ | ||
| 444 | server_url = strdup (optarg); | ||
| 445 | break; | ||
| 446 | case 'p': /* Server port */ | ||
| 447 | if (!is_intnonneg (optarg)) | ||
| 448 | usage2 (_("Invalid port number, expecting a non-negative number"), optarg); | ||
| 449 | else { | ||
| 450 | if( strtol(optarg, NULL, 10) > MAX_PORT) | ||
| 451 | usage2 (_("Invalid port number, supplied port number is too big"), optarg); | ||
| 452 | server_port = (unsigned short)strtol(optarg, NULL, 10); | ||
| 453 | } | ||
| 454 | break; | ||
| 455 | case 'a': /* authorization info */ | ||
| 456 | strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1); | ||
| 457 | user_auth[MAX_INPUT_BUFFER - 1] = 0; | ||
| 458 | break; | ||
| 459 | case 'A': /* useragent */ | ||
| 460 | snprintf (user_agent, DEFAULT_BUFFER_SIZE, optarg); | ||
| 461 | break; | ||
| 462 | case 'C': /* Check SSL cert validity */ | ||
| 463 | #ifdef LIBCURL_FEATURE_SSL | ||
| 464 | /* TODO: C:, check age of certificate for backward compatible | ||
| 465 | * behaviour, but we would later add more check conditions */ | ||
| 466 | check_cert = TRUE; | ||
| 467 | goto enable_ssl; | ||
| 468 | #endif | ||
| 469 | case 'J': /* use client certificate */ | ||
| 470 | #ifdef LIBCURL_FEATURE_SSL | ||
| 471 | test_file(optarg); | ||
| 472 | client_cert = optarg; | ||
| 473 | goto enable_ssl; | ||
| 474 | #endif | ||
| 475 | case 'K': /* use client private key */ | ||
| 476 | #ifdef LIBCURL_FEATURE_SSL | ||
| 477 | test_file(optarg); | ||
| 478 | client_privkey = optarg; | ||
| 479 | goto enable_ssl; | ||
| 480 | #endif | ||
| 481 | case 'S': /* use SSL */ | ||
| 482 | #ifdef LIBCURL_FEATURE_SSL | ||
| 483 | enable_ssl: | ||
| 484 | use_ssl = TRUE; | ||
| 485 | /* ssl_version initialized to CURL_SSLVERSION_TLSv1_0 as a default. Only set if it's non-zero. This helps when we include multiple | ||
| 486 | parameters, like -S and -C combinations */ | ||
| 487 | ssl_version = CURL_SSLVERSION_TLSv1_0; | ||
| 488 | if (c=='S' && optarg != NULL) { | ||
| 489 | int got_plus = strchr(optarg, '+') != NULL; | ||
| 490 | |||
| 491 | if (!strncmp (optarg, "1.2", 3)) | ||
| 492 | ssl_version = CURL_SSLVERSION_TLSv1_2; | ||
| 493 | else if (!strncmp (optarg, "1.1", 3)) | ||
| 494 | ssl_version = CURL_SSLVERSION_TLSv1_1; | ||
| 495 | else if (optarg[0] == '1') | ||
| 496 | ssl_version = CURL_SSLVERSION_TLSv1_0; | ||
| 497 | else if (optarg[0] == '3') | ||
| 498 | ssl_version = CURL_SSLVERSION_SSLv3; | ||
| 499 | else if (optarg[0] == '2') | ||
| 500 | ssl_version = CURL_SSLVERSION_SSLv2; | ||
| 501 | else | ||
| 502 | usage4 (_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2 (with optional '+' suffix)")); | ||
| 503 | } | ||
| 504 | if (server_port == DEFAULT_HTTP_PORT) | ||
| 505 | server_port = DEFAULT_HTTPS_PORT; | ||
| 506 | #else | ||
| 507 | /* -C -J and -K fall through to here without SSL */ | ||
| 508 | usage4 (_("Invalid option - SSL is not available")); | ||
| 509 | #endif | ||
| 510 | break; | ||
| 511 | case SNI_OPTION: /* --sni is parsed, but ignored, the default is TRUE with libcurl */ | ||
| 512 | use_sni = TRUE; | ||
| 513 | break; | ||
| 514 | case 'f': /* onredirect */ | ||
| 515 | if (!strcmp (optarg, "ok")) | ||
| 516 | onredirect = STATE_OK; | ||
| 517 | else if (!strcmp (optarg, "warning")) | ||
| 518 | onredirect = STATE_WARNING; | ||
| 519 | else if (!strcmp (optarg, "critical")) | ||
| 520 | onredirect = STATE_CRITICAL; | ||
| 521 | else if (!strcmp (optarg, "unknown")) | ||
| 522 | onredirect = STATE_UNKNOWN; | ||
| 523 | else if (!strcmp (optarg, "follow")) | ||
| 524 | onredirect = STATE_DEPENDENT; | ||
| 525 | else usage2 (_("Invalid onredirect option"), optarg); | ||
| 526 | //~ if (!strcmp (optarg, "stickyport")) | ||
| 527 | //~ onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST|STICKY_PORT; | ||
| 528 | //~ else if (!strcmp (optarg, "sticky")) | ||
| 529 | //~ onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST; | ||
| 530 | //~ else if (!strcmp (optarg, "follow")) | ||
| 531 | //~ onredirect = STATE_DEPENDENT, followsticky = STICKY_NONE; | ||
| 532 | if (verbose >= 2) | ||
| 533 | printf(_("* Following redirects set to %s\n"), state_text(onredirect)); | ||
| 534 | break; | ||
| 118 | case '?': | 535 | case '?': |
| 119 | /* print short usage statement if args not parsable */ | 536 | /* print short usage statement if args not parsable */ |
| 120 | usage5 (); | 537 | usage5 (); |
| 121 | break; | 538 | break; |
| 122 | } | 539 | } |
| 123 | } | 540 | } |
| 541 | |||
| 542 | c = optind; | ||
| 124 | 543 | ||
| 125 | return 0; | 544 | if (server_address == NULL && c < argc) |
| 545 | server_address = strdup (argv[c++]); | ||
| 546 | |||
| 547 | if (host_name == NULL && c < argc) | ||
| 548 | host_name = strdup (argv[c++]); | ||
| 549 | |||
| 550 | if (server_address == NULL) { | ||
| 551 | if (host_name == NULL) | ||
| 552 | usage4 (_("You must specify a server address or host name")); | ||
| 553 | else | ||
| 554 | server_address = strdup (host_name); | ||
| 555 | } | ||
| 556 | |||
| 557 | set_thresholds(&thlds, warning_thresholds, critical_thresholds); | ||
| 558 | |||
| 559 | if (critical_thresholds && thlds->critical->end>(double)socket_timeout) | ||
| 560 | socket_timeout = (int)thlds->critical->end + 1; | ||
| 561 | if (verbose >= 2) | ||
| 562 | printf ("* Socket timeout set to %d seconds\n", socket_timeout); | ||
| 563 | |||
| 564 | //~ if (http_method == NULL) | ||
| 565 | //~ http_method = strdup ("GET"); | ||
| 566 | |||
| 567 | if (client_cert && !client_privkey) | ||
| 568 | usage4 (_("If you use a client certificate you must also specify a private key file")); | ||
| 569 | |||
| 570 | //~ if (virtual_port == 0) | ||
| 571 | //~ virtual_port = server_port; | ||
| 572 | |||
| 573 | return TRUE; | ||
| 126 | } | 574 | } |
| 127 | 575 | ||
| 128 | void | 576 | void |
| @@ -133,32 +581,91 @@ print_help (void) | |||
| 133 | printf ("Copyright (c) 2017 Andreas Baumann <abaumann@yahoo.com>\n"); | 581 | printf ("Copyright (c) 2017 Andreas Baumann <abaumann@yahoo.com>\n"); |
| 134 | printf (COPYRIGHT, copyright, email); | 582 | printf (COPYRIGHT, copyright, email); |
| 135 | 583 | ||
| 136 | printf ("%s\n", _("This plugin tests the HTTP(S) service on the specified host.")); | 584 | printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test")); |
| 137 | printf ("%s\n", _("It makes use of libcurl to do so.")); | 585 | printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for")); |
| 586 | printf ("%s\n", _("strings and regular expressions, check connection times, and report on")); | ||
| 587 | printf ("%s\n", _("certificate expiration times.")); | ||
| 588 | printf ("\n"); | ||
| 589 | printf ("%s\n", _("It makes use of libcurl to do so. It tries to be as compatible to check_http")); | ||
| 590 | printf ("%s\n", _("as possible.")); | ||
| 138 | 591 | ||
| 139 | printf ("\n\n"); | 592 | printf ("\n\n"); |
| 140 | 593 | ||
| 141 | print_usage(); | 594 | print_usage(); |
| 595 | |||
| 142 | printf (_("NOTE: One or both of -H and -I must be specified")); | 596 | printf (_("NOTE: One or both of -H and -I must be specified")); |
| 143 | 597 | ||
| 144 | printf ("\n"); | 598 | printf ("\n"); |
| 145 | 599 | ||
| 146 | printf (UT_HELP_VRSN); | 600 | printf (UT_HELP_VRSN); |
| 601 | printf (UT_EXTRA_OPTS); | ||
| 602 | |||
| 603 | printf (" %s\n", "-H, --hostname=ADDRESS"); | ||
| 604 | printf (" %s\n", _("Host name argument for servers using host headers (virtual host)")); | ||
| 605 | printf (" %s\n", _("Append a port to include it in the header (eg: example.com:5000)")); | ||
| 606 | printf (" %s\n", "-I, --IP-address=ADDRESS"); | ||
| 607 | printf (" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup).")); | ||
| 608 | printf (" %s\n", "-p, --port=INTEGER"); | ||
| 609 | printf (" %s", _("Port number (default: ")); | ||
| 610 | printf ("%d)\n", DEFAULT_HTTP_PORT); | ||
| 611 | |||
| 612 | #ifdef LIBCURL_FEATURE_SSL | ||
| 613 | printf (" %s\n", "-S, --ssl=VERSION[+]"); | ||
| 614 | printf (" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents")); | ||
| 615 | printf (" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,")); | ||
| 616 | printf (" %s\n", _("1.2 = TLSv1.2). With a '+' suffix, newer versions are also accepted.")); | ||
| 617 | printf (" %s\n", _("Note: SSLv2 and SSLv3 are deprecated and are usually disabled in libcurl")); | ||
| 618 | printf (" %s\n", "--sni"); | ||
| 619 | printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)")); | ||
| 620 | #if LIBCURL_VERSION_NUM >= 0x071801 | ||
| 621 | printf (" %s\n", _("Note: --sni is the default in libcurl as SSLv2 and SSLV3 are deprecated and")); | ||
| 622 | printf (" %s\n", _(" SNI only really works since TLSv1.0")); | ||
| 623 | #else | ||
| 624 | printf (" %s\n", _("Note: SNI is not supported in libcurl before 7.18.1")); | ||
| 625 | #endif | ||
| 626 | printf (" %s\n", "-C, --certificate"); | ||
| 627 | printf (" %s\n", _("Check validity of certificate")); | ||
| 628 | printf (" %s\n", "-J, --client-cert=FILE"); | ||
| 629 | printf (" %s\n", _("Name of file that contains the client certificate (PEM format)")); | ||
| 630 | printf (" %s\n", _("to be used in establishing the SSL session")); | ||
| 631 | printf (" %s\n", "-K, --private-key=FILE"); | ||
| 632 | printf (" %s\n", _("Name of file containing the private key (PEM format)")); | ||
| 633 | printf (" %s\n", _("matching the client certificate")); | ||
| 634 | #endif | ||
| 635 | |||
| 636 | printf (" %s\n", "-u, --url=PATH"); | ||
| 637 | printf (" %s\n", _("URL to GET or POST (default: /)")); | ||
| 638 | |||
| 639 | printf (" %s\n", "-a, --authorization=AUTH_PAIR"); | ||
| 640 | printf (" %s\n", _("Username:password on sites with basic authentication")); | ||
| 641 | printf (" %s\n", "-A, --useragent=STRING"); | ||
| 642 | printf (" %s\n", _("String to be sent in http header as \"User Agent\"")); | ||
| 643 | printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>"); | ||
| 644 | printf (" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the")); | ||
| 645 | printf (" %s\n", _("specified IP address. stickyport also ensures port stays the same.")); | ||
| 646 | |||
| 647 | printf (UT_WARN_CRIT); | ||
| 648 | |||
| 649 | printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); | ||
| 650 | |||
| 147 | printf (UT_VERBOSE); | 651 | printf (UT_VERBOSE); |
| 148 | 652 | ||
| 149 | printf (UT_SUPPORT); | 653 | printf (UT_SUPPORT); |
| 150 | |||
| 151 | printf ("%s\n", _("WARNING: check_curl is experimental. Please use")); | ||
| 152 | printf ("%s\n\n", _("check_http if you need a stable version.")); | ||
| 153 | } | 654 | } |
| 154 | 655 | ||
| 155 | void | 656 | void |
| 156 | print_usage (void) | 657 | print_usage (void) |
| 157 | { | 658 | { |
| 659 | printf ("%s\n", _("Usage:")); | ||
| 660 | printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname); | ||
| 661 | printf (" [-J <client certificate file>] [-K <private key>]\n"); | ||
| 662 | printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-a auth]\n"); | ||
| 663 | printf (" [-f <ok|warning|critcal|follow>]\n"); | ||
| 664 | printf (" [-A string] [-S <version>] [-C]\n"); | ||
| 665 | printf (" [-v verbose]\n", progname); | ||
| 666 | printf ("\n"); | ||
| 158 | printf ("%s\n", _("WARNING: check_curl is experimental. Please use")); | 667 | printf ("%s\n", _("WARNING: check_curl is experimental. Please use")); |
| 159 | printf ("%s\n\n", _("check_http if you need a stable version.")); | 668 | printf ("%s\n\n", _("check_http if you need a stable version.")); |
| 160 | printf ("%s\n", _("Usage:")); | ||
| 161 | printf (" %s [-v verbose]\n", progname); | ||
| 162 | } | 669 | } |
| 163 | 670 | ||
| 164 | void | 671 | void |
| @@ -166,3 +673,118 @@ print_curl_version (void) | |||
| 166 | { | 673 | { |
| 167 | printf( "%s\n", curl_version()); | 674 | printf( "%s\n", curl_version()); |
| 168 | } | 675 | } |
| 676 | |||
| 677 | int | ||
| 678 | curlhelp_initbuffer (curlhelp_curlbuf *buf) | ||
| 679 | { | ||
| 680 | buf->bufsize = DEFAULT_BUFFER_SIZE; | ||
| 681 | buf->buflen = 0; | ||
| 682 | buf->buf = (char *)malloc ((size_t)buf->bufsize); | ||
| 683 | if (buf->buf == NULL) return -1; | ||
| 684 | return 0; | ||
| 685 | } | ||
| 686 | |||
| 687 | int | ||
| 688 | curlhelp_buffer_callback (void *buffer, size_t size, size_t nmemb, void *stream) | ||
| 689 | { | ||
| 690 | curlhelp_curlbuf *buf = (curlhelp_curlbuf *)stream; | ||
| 691 | |||
| 692 | while (buf->bufsize < buf->buflen + size * nmemb + 1) { | ||
| 693 | buf->bufsize *= buf->bufsize * 2; | ||
| 694 | buf->buf = (char *)realloc (buf->buf, buf->bufsize); | ||
| 695 | if (buf->buf == NULL) return -1; | ||
| 696 | } | ||
| 697 | |||
| 698 | memcpy (buf->buf + buf->buflen, buffer, size * nmemb); | ||
| 699 | buf->buflen += size * nmemb; | ||
| 700 | buf->buf[buf->buflen] = '\0'; | ||
| 701 | |||
| 702 | return (int)(size * nmemb); | ||
| 703 | } | ||
| 704 | |||
| 705 | void | ||
| 706 | curlhelp_freebuffer (curlhelp_curlbuf *buf) | ||
| 707 | { | ||
| 708 | free (buf->buf); | ||
| 709 | buf->buf = NULL; | ||
| 710 | } | ||
| 711 | |||
| 712 | /* TODO: when redirecting we get more than one HTTP header, make sure | ||
| 713 | * we parse the last one | ||
| 714 | */ | ||
| 715 | int | ||
| 716 | curlhelp_parse_statusline (char *buf, curlhelp_statusline *status_line) | ||
| 717 | { | ||
| 718 | char *first_line_end; | ||
| 719 | char *p; | ||
| 720 | size_t first_line_len; | ||
| 721 | char *pp; | ||
| 722 | |||
| 723 | first_line_end = strstr(buf, "\r\n"); | ||
| 724 | if (first_line_end == NULL) return -1; | ||
| 725 | |||
| 726 | first_line_len = (size_t)(first_line_end - buf); | ||
| 727 | status_line->first_line = (char *)malloc (first_line_len + 1); | ||
| 728 | if (status_line->first_line == NULL) return -1; | ||
| 729 | memcpy (status_line->first_line, buf, first_line_len); | ||
| 730 | status_line->first_line[first_line_len] = '\0'; | ||
| 731 | |||
| 732 | /* protocol and version: "HTTP/x.x" SP */ | ||
| 733 | |||
| 734 | p = strtok(status_line->first_line, "/"); | ||
| 735 | if( p == NULL ) { free( status_line->first_line ); return -1; } | ||
| 736 | if( strcmp( p, "HTTP" ) != 0 ) { free( status_line->first_line ); return -1; } | ||
| 737 | |||
| 738 | p = strtok( NULL, "." ); | ||
| 739 | if( p == NULL ) { free( status_line->first_line ); return -1; } | ||
| 740 | status_line->http_major = (int)strtol( p, &pp, 10 ); | ||
| 741 | if( *pp != '\0' ) { free( status_line->first_line ); return -1; } | ||
| 742 | |||
| 743 | p = strtok( NULL, " " ); | ||
| 744 | if( p == NULL ) { free( status_line->first_line ); return -1; } | ||
| 745 | status_line->http_minor = (int)strtol( p, &pp, 10 ); | ||
| 746 | if( *pp != '\0' ) { free( status_line->first_line ); return -1; } | ||
| 747 | |||
| 748 | /* status code: "404" or "404.1", then SP */ | ||
| 749 | |||
| 750 | p = strtok( NULL, " ." ); | ||
| 751 | if( p == NULL ) { free( status_line->first_line ); return -1; } | ||
| 752 | if( strchr( p, '.' ) != NULL ) { | ||
| 753 | char *ppp; | ||
| 754 | ppp = strtok( p, "." ); | ||
| 755 | status_line->http_code = (int)strtol( ppp, &pp, 10 ); | ||
| 756 | if( *pp != '\0' ) { free( status_line->first_line ); return -1; } | ||
| 757 | |||
| 758 | ppp = strtok( NULL, "" ); | ||
| 759 | status_line->http_subcode = (int)strtol( ppp, &pp, 10 ); | ||
| 760 | if( *pp != '\0' ) { free( status_line->first_line ); return -1; } | ||
| 761 | } else { | ||
| 762 | status_line->http_code = (int)strtol( p, &pp, 10 ); | ||
| 763 | status_line->http_subcode = -1; | ||
| 764 | if( *pp != '\0' ) { free( status_line->first_line ); return -1; } | ||
| 765 | } | ||
| 766 | |||
| 767 | /* Human readable message: "Not Found" CRLF */ | ||
| 768 | |||
| 769 | p = strtok( NULL, "" ); | ||
| 770 | if( p == NULL ) { free( status_line->first_line ); return -1; } | ||
| 771 | status_line->msg = p; | ||
| 772 | |||
| 773 | return 0; | ||
| 774 | } | ||
| 775 | |||
| 776 | void | ||
| 777 | curlhelp_free_statusline (curlhelp_statusline *status_line) | ||
| 778 | { | ||
| 779 | free (status_line->first_line); | ||
| 780 | } | ||
| 781 | |||
| 782 | void | ||
| 783 | remove_newlines (char *s) | ||
| 784 | { | ||
| 785 | char *p; | ||
| 786 | |||
| 787 | for (p = s; *p != '\0'; p++) | ||
| 788 | if (*p == '\r' || *p == '\n') | ||
| 789 | *p = ' '; | ||
| 790 | } | ||
