diff options
Diffstat (limited to 'plugins')
-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 be024fe..e4230dc 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 | } | ||