1 /******************************************************************************
3 * Nagios check_http plugin
6 * Copyright (c) 1999-2006 nagios-plugins team
8 * Last Modified: $Date$
12 * This file contains the check_http plugin
14 * This plugin tests the HTTP service on the specified host. It can test
15 * normal (http) and secure (https) servers, follow redirects, search for
16 * strings and regular expressions, check connection times, and report on
17 * certificate expiration times.
20 * License Information:
22 * This program is free software; you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation; either version 2 of the License, or
25 * (at your option) any later version.
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software
34 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
38 ******************************************************************************/
39 /* splint -I. -I../../plugins -I../../lib/ -I/usr/kerberos/include/ ../../plugins/check_http.c */
41 const char *progname
= "check_http";
42 const char *revision
= "$Revision$";
43 const char *copyright
= "1999-2006";
44 const char *email
= "nagiosplug-devel@lists.sourceforge.net";
50 #define INPUT_DELIMITER ";"
52 #define HTTP_EXPECT "HTTP/1."
54 MAX_IPV4_HOSTLENGTH
= 255,
60 int check_cert
= FALSE
;
64 # define my_recv(buf, len) ((use_ssl) ? np_net_ssl_read(buf, len) : read(sd, buf, len))
65 # define my_send(buf, len) ((use_ssl) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0))
66 #else /* ifndef HAVE_SSL */
67 # define my_recv(buf, len) read(sd, buf, len)
68 # define my_send(buf, len) send(sd, buf, len, 0)
79 regmatch_t pmatch
[REGS
];
80 char regexp
[MAX_RE_SIZE
];
81 char errbuf
[MAX_INPUT_BUFFER
];
82 int cflags
= REG_NOSUB
| REG_EXTENDED
| REG_NEWLINE
;
91 int specify_port
= FALSE
;
92 int server_port
= HTTP_PORT
;
93 char server_port_text
[6] = "";
94 char server_type
[6] = "http";
99 int server_url_length
;
100 int server_expect_yn
= 0;
101 char server_expect
[MAX_INPUT_BUFFER
] = HTTP_EXPECT
;
102 char string_expect
[MAX_INPUT_BUFFER
] = "";
103 double warning_time
= 0;
104 int check_warning_time
= FALSE
;
105 double critical_time
= 0;
106 int check_critical_time
= FALSE
;
107 char user_auth
[MAX_INPUT_BUFFER
] = "";
108 int display_html
= FALSE
;
109 char **http_opt_headers
;
110 int http_opt_headers_count
= 0;
111 int onredirect
= STATE_OK
;
115 int min_page_len
= 0;
116 int max_page_len
= 0;
120 char *http_post_data
;
121 char *http_content_type
;
122 char buffer
[MAX_INPUT_BUFFER
];
124 int process_arguments (int, char **);
125 static char *base64 (const char *bin
, size_t len
);
126 int check_http (void);
127 void redir (char *pos
, char *status_line
);
128 int server_type_check(const char *type
);
129 int server_port_check(int ssl_flag
);
130 char *perfd_time (double microsec
);
131 char *perfd_size (int page_len
);
132 void print_help (void);
133 void print_usage (void);
136 main (int argc
, char **argv
)
138 int result
= STATE_UNKNOWN
;
140 /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
141 server_url
= strdup(HTTP_URL
);
142 server_url_length
= strlen(server_url
);
143 asprintf (&user_agent
, "User-Agent: check_http/%s (nagios-plugins %s)",
144 clean_revstring (revision
), VERSION
);
146 if (process_arguments (argc
, argv
) == ERROR
)
147 usage4 (_("Could not parse arguments"));
149 if (display_html
== TRUE
)
150 printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">",
151 use_ssl
? "https" : "http", host_name
,
152 server_port
, server_url
);
154 /* initialize alarm signal handling, set socket timeout, start timer */
155 (void) signal (SIGALRM
, socket_timeout_alarm_handler
);
156 (void) alarm (socket_timeout
);
157 gettimeofday (&tv
, NULL
);
159 result
= check_http ();
165 /* process command-line arguments */
167 process_arguments (int argc
, char **argv
)
172 INVERT_REGEX
= CHAR_MAX
+ 1
176 static struct option longopts
[] = {
178 {"link", no_argument
, 0, 'L'},
179 {"nohtml", no_argument
, 0, 'n'},
180 {"ssl", no_argument
, 0, 'S'},
181 {"post", required_argument
, 0, 'P'},
182 {"IP-address", required_argument
, 0, 'I'},
183 {"url", required_argument
, 0, 'u'},
184 {"port", required_argument
, 0, 'p'},
185 {"authorization", required_argument
, 0, 'a'},
186 {"string", required_argument
, 0, 's'},
187 {"expect", required_argument
, 0, 'e'},
188 {"regex", required_argument
, 0, 'r'},
189 {"ereg", required_argument
, 0, 'r'},
190 {"eregi", required_argument
, 0, 'R'},
191 {"linespan", no_argument
, 0, 'l'},
192 {"onredirect", required_argument
, 0, 'f'},
193 {"certificate", required_argument
, 0, 'C'},
194 {"useragent", required_argument
, 0, 'A'},
195 {"header", required_argument
, 0, 'k'},
196 {"no-body", no_argument
, 0, 'N'},
197 {"max-age", required_argument
, 0, 'M'},
198 {"content-type", required_argument
, 0, 'T'},
199 {"pagesize", required_argument
, 0, 'm'},
200 {"invert-regex", no_argument
, NULL
, INVERT_REGEX
},
201 {"use-ipv4", no_argument
, 0, '4'},
202 {"use-ipv6", no_argument
, 0, '6'},
209 for (c
= 1; c
< argc
; c
++) {
210 if (strcmp ("-to", argv
[c
]) == 0)
211 strcpy (argv
[c
], "-t");
212 if (strcmp ("-hn", argv
[c
]) == 0)
213 strcpy (argv
[c
], "-H");
214 if (strcmp ("-wt", argv
[c
]) == 0)
215 strcpy (argv
[c
], "-w");
216 if (strcmp ("-ct", argv
[c
]) == 0)
217 strcpy (argv
[c
], "-c");
218 if (strcmp ("-nohtml", argv
[c
]) == 0)
219 strcpy (argv
[c
], "-n");
223 c
= getopt_long (argc
, argv
, "Vvh46t:c:w:A:k:H:P:T:I:a:e:p:s:R:r:u:f:C:nlLSm:M:N", longopts
, &option
);
224 if (c
== -1 || c
== EOF
)
228 case '?': /* usage */
235 case 'V': /* version */
236 print_revision (progname
, revision
);
239 case 't': /* timeout period */
240 if (!is_intnonneg (optarg
))
241 usage2 (_("Timeout interval must be a positive integer"), optarg
);
243 socket_timeout
= atoi (optarg
);
245 case 'c': /* critical time threshold */
246 if (!is_nonnegative (optarg
))
247 usage2 (_("Critical threshold must be integer"), optarg
);
249 critical_time
= strtod (optarg
, NULL
);
250 check_critical_time
= TRUE
;
253 case 'w': /* warning time threshold */
254 if (!is_nonnegative (optarg
))
255 usage2 (_("Warning threshold must be integer"), optarg
);
257 warning_time
= strtod (optarg
, NULL
);
258 check_warning_time
= TRUE
;
261 case 'A': /* User Agent String */
262 asprintf (&user_agent
, "User-Agent: %s", optarg
);
264 case 'k': /* Additional headers */
265 if (http_opt_headers_count
== 0)
266 http_opt_headers
= malloc (sizeof (char *) * (++http_opt_headers_count
));
268 http_opt_headers
= realloc (http_opt_headers
, sizeof (char *) * (++http_opt_headers_count
));
269 http_opt_headers
[http_opt_headers_count
- 1] = optarg
;
270 /* asprintf (&http_opt_headers, "%s", optarg); */
272 case 'L': /* show html link */
275 case 'n': /* do not show html link */
276 display_html
= FALSE
;
278 case 'C': /* Check SSL cert validity */
280 if (!is_intnonneg (optarg
))
281 usage2 (_("Invalid certificate expiration period"), optarg
);
283 days_till_exp
= atoi (optarg
);
286 /* Fall through to -S option */
288 case 'S': /* use SSL */
290 usage4 (_("Invalid option - SSL is not available"));
293 if (specify_port
== FALSE
)
294 server_port
= HTTPS_PORT
;
296 case 'f': /* onredirect */
297 if (!strcmp (optarg
, "follow"))
298 onredirect
= STATE_DEPENDENT
;
299 if (!strcmp (optarg
, "unknown"))
300 onredirect
= STATE_UNKNOWN
;
301 if (!strcmp (optarg
, "ok"))
302 onredirect
= STATE_OK
;
303 if (!strcmp (optarg
, "warning"))
304 onredirect
= STATE_WARNING
;
305 if (!strcmp (optarg
, "critical"))
306 onredirect
= STATE_CRITICAL
;
308 printf(_("option f:%d \n"), onredirect
);
310 /* Note: H, I, and u must be malloc'd or will fail on redirects */
311 case 'H': /* Host Name (virtual host) */
312 host_name
= strdup (optarg
);
313 if (strstr (optarg
, ":"))
314 sscanf (optarg
, "%*[^:]:%d", &server_port
);
316 case 'I': /* Server IP-address */
317 server_address
= strdup (optarg
);
319 case 'u': /* URL path */
320 server_url
= strdup (optarg
);
321 server_url_length
= strlen (server_url
);
323 case 'p': /* Server port */
324 if (!is_intnonneg (optarg
))
325 usage2 (_("Invalid port number"), optarg
);
327 server_port
= atoi (optarg
);
331 case 'a': /* authorization info */
332 strncpy (user_auth
, optarg
, MAX_INPUT_BUFFER
- 1);
333 user_auth
[MAX_INPUT_BUFFER
- 1] = 0;
335 case 'P': /* HTTP POST data in URL encoded format */
336 if (http_method
|| http_post_data
) break;
337 http_method
= strdup("POST");
338 http_post_data
= strdup (optarg
);
340 case 's': /* string or substring */
341 strncpy (string_expect
, optarg
, MAX_INPUT_BUFFER
- 1);
342 string_expect
[MAX_INPUT_BUFFER
- 1] = 0;
344 case 'e': /* string or substring */
345 strncpy (server_expect
, optarg
, MAX_INPUT_BUFFER
- 1);
346 server_expect
[MAX_INPUT_BUFFER
- 1] = 0;
347 server_expect_yn
= 1;
349 case 'T': /* Content-type */
350 asprintf (&http_content_type
, "%s", optarg
);
352 case 'l': /* linespan */
353 cflags
&= ~REG_NEWLINE
;
355 case 'R': /* regex */
357 case 'r': /* regex */
358 strncpy (regexp
, optarg
, MAX_RE_SIZE
- 1);
359 regexp
[MAX_RE_SIZE
- 1] = 0;
360 errcode
= regcomp (&preg
, regexp
, cflags
);
362 (void) regerror (errcode
, &preg
, errbuf
, MAX_INPUT_BUFFER
);
363 printf (_("Could Not Compile Regular Expression: %s"), errbuf
);
371 address_family
= AF_INET
;
375 address_family
= AF_INET6
;
377 usage4 (_("IPv6 support not available"));
380 case 'v': /* verbose */
383 case 'm': /* min_page_length */
386 if (strchr(optarg
, ':') != (char *)NULL
) {
387 /* range, so get two values, min:max */
388 tmp
= strtok(optarg
, ":");
390 printf("Bad format: try \"-m min:max\"\n");
391 exit (STATE_WARNING
);
393 min_page_len
= atoi(tmp
);
395 tmp
= strtok(NULL
, ":");
397 printf("Bad format: try \"-m min:max\"\n");
398 exit (STATE_WARNING
);
400 max_page_len
= atoi(tmp
);
402 min_page_len
= atoi (optarg
);
405 case 'N': /* no-body */
408 case 'M': /* max-age */
410 int L
= strlen(optarg
);
411 if (L
&& optarg
[L
-1] == 'm')
412 maximum_age
= atoi (optarg
) * 60;
413 else if (L
&& optarg
[L
-1] == 'h')
414 maximum_age
= atoi (optarg
) * 60 * 60;
415 else if (L
&& optarg
[L
-1] == 'd')
416 maximum_age
= atoi (optarg
) * 60 * 60 * 24;
417 else if (L
&& (optarg
[L
-1] == 's' ||
418 isdigit (optarg
[L
-1])))
419 maximum_age
= atoi (optarg
);
421 fprintf (stderr
, "unparsable max-age: %s\n", optarg
);
422 exit (STATE_WARNING
);
431 if (server_address
== NULL
&& c
< argc
)
432 server_address
= strdup (argv
[c
++]);
434 if (host_name
== NULL
&& c
< argc
)
435 host_name
= strdup (argv
[c
++]);
437 if (server_address
== NULL
) {
438 if (host_name
== NULL
)
439 usage4 (_("You must specify a server address or host name"));
441 server_address
= strdup (host_name
);
444 if (check_critical_time
&& critical_time
>(double)socket_timeout
)
445 socket_timeout
= (int)critical_time
+ 1;
447 if (http_method
== NULL
)
448 http_method
= strdup ("GET");
455 /* written by lauri alanko */
457 base64 (const char *bin
, size_t len
)
460 char *buf
= (char *) malloc ((len
+ 2) / 3 * 4 + 1);
463 char BASE64_END
= '=';
464 char base64_table
[64];
465 strncpy (base64_table
, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 64);
467 while (j
< len
- 2) {
468 buf
[i
++] = base64_table
[bin
[j
] >> 2];
469 buf
[i
++] = base64_table
[((bin
[j
] & 3) << 4) | (bin
[j
+ 1] >> 4)];
470 buf
[i
++] = base64_table
[((bin
[j
+ 1] & 15) << 2) | (bin
[j
+ 2] >> 6)];
471 buf
[i
++] = base64_table
[bin
[j
+ 2] & 63];
477 buf
[i
++] = base64_table
[bin
[j
] >> 2];
478 buf
[i
++] = base64_table
[(bin
[j
] & 3) << 4];
479 buf
[i
++] = BASE64_END
;
480 buf
[i
++] = BASE64_END
;
483 buf
[i
++] = base64_table
[bin
[j
] >> 2];
484 buf
[i
++] = base64_table
[((bin
[j
] & 3) << 4) | (bin
[j
+ 1] >> 4)];
485 buf
[i
++] = base64_table
[(bin
[j
+ 1] & 15) << 2];
486 buf
[i
++] = BASE64_END
;
498 /* Returns 1 if we're done processing the document body; 0 to keep going */
500 document_headers_done (char *full_page
)
504 for (body
= full_page
; *body
; body
++) {
505 if (!strncmp (body
, "\n\n", 2) || !strncmp (body
, "\n\r\n", 3))
510 return 0; /* haven't read end of headers yet */
512 full_page
[body
- full_page
] = 0;
517 parse_time_string (const char *string
)
521 memset (&tm
, 0, sizeof(tm
));
523 /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */
525 if (isupper (string
[0]) && /* Tue */
526 islower (string
[1]) &&
527 islower (string
[2]) &&
530 (isdigit(string
[5]) || string
[5] == ' ') && /* 25 */
531 isdigit (string
[6]) &&
533 isupper (string
[8]) && /* Dec */
534 islower (string
[9]) &&
535 islower (string
[10]) &&
537 isdigit (string
[12]) && /* 2001 */
538 isdigit (string
[13]) &&
539 isdigit (string
[14]) &&
540 isdigit (string
[15]) &&
542 isdigit (string
[17]) && /* 02: */
543 isdigit (string
[18]) &&
545 isdigit (string
[20]) && /* 59: */
546 isdigit (string
[21]) &&
548 isdigit (string
[23]) && /* 03 */
549 isdigit (string
[24]) &&
551 'G' == string
[26] && /* GMT */
552 'M' == string
[27] && /* GMT */
555 tm
.tm_sec
= 10 * (string
[23]-'0') + (string
[24]-'0');
556 tm
.tm_min
= 10 * (string
[20]-'0') + (string
[21]-'0');
557 tm
.tm_hour
= 10 * (string
[17]-'0') + (string
[18]-'0');
558 tm
.tm_mday
= 10 * (string
[5] == ' ' ? 0 : string
[5]-'0') + (string
[6]-'0');
559 tm
.tm_mon
= (!strncmp (string
+8, "Jan", 3) ? 0 :
560 !strncmp (string
+8, "Feb", 3) ? 1 :
561 !strncmp (string
+8, "Mar", 3) ? 2 :
562 !strncmp (string
+8, "Apr", 3) ? 3 :
563 !strncmp (string
+8, "May", 3) ? 4 :
564 !strncmp (string
+8, "Jun", 3) ? 5 :
565 !strncmp (string
+8, "Jul", 3) ? 6 :
566 !strncmp (string
+8, "Aug", 3) ? 7 :
567 !strncmp (string
+8, "Sep", 3) ? 8 :
568 !strncmp (string
+8, "Oct", 3) ? 9 :
569 !strncmp (string
+8, "Nov", 3) ? 10 :
570 !strncmp (string
+8, "Dec", 3) ? 11 :
572 tm
.tm_year
= ((1000 * (string
[12]-'0') +
573 100 * (string
[13]-'0') +
574 10 * (string
[14]-'0') +
578 tm
.tm_isdst
= 0; /* GMT is never in DST, right? */
580 if (tm
.tm_mon
< 0 || tm
.tm_mday
< 1 || tm
.tm_mday
> 31)
584 This is actually wrong: we need to subtract the local timezone
585 offset from GMT from this value. But, that's ok in this usage,
586 because we only comparing these two GMT dates against each other,
587 so it doesn't matter what time zone we parse them in.
591 if (t
== (time_t) -1) t
= 0;
594 const char *s
= string
;
595 while (*s
&& *s
!= '\r' && *s
!= '\n')
596 fputc (*s
++, stdout
);
597 printf (" ==> %lu\n", (unsigned long) t
);
610 check_document_dates (const char *headers
)
613 char *server_date
= 0;
614 char *document_date
= 0;
618 const char *field
= s
;
619 const char *value
= 0;
621 /* Find the end of the header field */
622 while (*s
&& !isspace(*s
) && *s
!= ':')
625 /* Remember the header value, if any. */
629 /* Skip to the end of the header, including continuation lines. */
630 while (*s
&& !(*s
== '\n' && (s
[1] != ' ' && s
[1] != '\t')))
634 /* Process this header. */
635 if (value
&& value
> field
+2) {
636 char *ff
= (char *) malloc (value
-field
);
638 while (field
< value
-1)
639 *ss
++ = tolower(*field
++);
642 if (!strcmp (ff
, "date") || !strcmp (ff
, "last-modified")) {
644 while (*value
&& isspace (*value
))
646 for (e
= value
; *e
&& *e
!= '\r' && *e
!= '\n'; e
++)
648 ss
= (char *) malloc (e
- value
+ 1);
649 strncpy (ss
, value
, e
- value
);
651 if (!strcmp (ff
, "date")) {
652 if (server_date
) free (server_date
);
655 if (document_date
) free (document_date
);
663 /* Done parsing the body. Now check the dates we (hopefully) parsed. */
664 if (!server_date
|| !*server_date
) {
665 die (STATE_UNKNOWN
, _("HTTP UNKNOWN - Server date unknown\n"));
666 } else if (!document_date
|| !*document_date
) {
667 die (STATE_CRITICAL
, _("HTTP CRITICAL - Document modification date unknown\n"));
669 time_t srv_data
= parse_time_string (server_date
);
670 time_t doc_data
= parse_time_string (document_date
);
673 die (STATE_CRITICAL
, _("HTTP CRITICAL - Server date \"%100s\" unparsable"), server_date
);
674 } else if (doc_data
<= 0) {
675 die (STATE_CRITICAL
, _("HTTP CRITICAL - Document date \"%100s\" unparsable"), document_date
);
676 } else if (doc_data
> srv_data
+ 30) {
677 die (STATE_CRITICAL
, _("HTTP CRITICAL - Document is %d seconds in the future\n"), (int)doc_data
- (int)srv_data
);
678 } else if (doc_data
< srv_data
- maximum_age
) {
679 int n
= (srv_data
- doc_data
);
680 if (n
> (60 * 60 * 24 * 2))
682 _("HTTP CRITICAL - Last modified %.1f days ago\n"),
683 ((float) n
) / (60 * 60 * 24));
686 _("HTTP CRITICAL - Last modified %d:%02d:%02d ago\n"),
687 n
/ (60 * 60), (n
/ 60) % 60, n
% 60);
691 free (document_date
);
696 get_content_length (const char *headers
)
699 int content_length
= 0;
703 const char *field
= s
;
704 const char *value
= 0;
706 /* Find the end of the header field */
707 while (*s
&& !isspace(*s
) && *s
!= ':')
710 /* Remember the header value, if any. */
714 /* Skip to the end of the header, including continuation lines. */
715 while (*s
&& !(*s
== '\n' && (s
[1] != ' ' && s
[1] != '\t')))
719 /* Process this header. */
720 if (value
&& value
> field
+2) {
721 char *ff
= (char *) malloc (value
-field
);
723 while (field
< value
-1)
724 *ss
++ = tolower(*field
++);
727 if (!strcmp (ff
, "content-length")) {
729 while (*value
&& isspace (*value
))
731 for (e
= value
; *e
&& *e
!= '\r' && *e
!= '\n'; e
++)
733 ss
= (char *) malloc (e
- value
+ 1);
734 strncpy (ss
, value
, e
- value
);
736 content_length
= atoi(ss
);
742 return (content_length
);
763 int result
= STATE_UNKNOWN
;
765 /* try to connect to the host at the given port number */
766 if (my_tcp_connect (server_address
, server_port
, &sd
) != STATE_OK
)
767 die (STATE_CRITICAL
, _("HTTP CRITICAL - Unable to open TCP socket\n"));
769 if (use_ssl
== TRUE
) {
771 if (check_cert
== TRUE
) {
772 result
= np_net_ssl_check_cert(days_till_exp
);
773 np_net_ssl_cleanup();
778 #endif /* HAVE_SSL */
780 asprintf (&buf
, "%s %s HTTP/1.0\r\n%s\r\n", http_method
, server_url
, user_agent
);
782 /* optionally send the host header info */
784 asprintf (&buf
, "%sHost: %s\r\n", buf
, host_name
);
786 /* optionally send any other header tag */
787 if (http_opt_headers_count
) {
788 for (i
= 0; i
< http_opt_headers_count
; i
++) {
789 for ((pos
= strtok(http_opt_headers
[i
], INPUT_DELIMITER
)); pos
; (pos
= strtok(NULL
, INPUT_DELIMITER
)))
790 asprintf (&buf
, "%s%s\r\n", buf
, pos
);
792 free(http_opt_headers
);
795 /* optionally send the authentication info */
796 if (strlen(user_auth
)) {
797 auth
= base64 (user_auth
, strlen (user_auth
));
798 asprintf (&buf
, "%sAuthorization: Basic %s\r\n", buf
, auth
);
801 /* either send http POST data */
802 if (http_post_data
) {
803 if (http_content_type
) {
804 asprintf (&buf
, "%sContent-Type: %s\r\n", buf
, http_content_type
);
806 asprintf (&buf
, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf
);
809 asprintf (&buf
, "%sContent-Length: %i\r\n\r\n", buf
, (int)strlen (http_post_data
));
810 asprintf (&buf
, "%s%s%s", buf
, http_post_data
, CRLF
);
813 /* or just a newline so the server knows we're done with the request */
814 asprintf (&buf
, "%s%s", buf
, CRLF
);
817 if (verbose
) printf ("%s\n", buf
);
818 my_send (buf
, strlen (buf
));
821 full_page
= strdup("");
822 while ((i
= my_recv (buffer
, MAX_INPUT_BUFFER
-1)) > 0) {
824 asprintf (&full_page
, "%s%s", full_page
, buffer
);
827 if (no_body
&& document_headers_done (full_page
)) {
833 if (i
< 0 && errno
!= ECONNRESET
) {
837 sslerr=SSL_get_error(ssl, i);
838 if ( sslerr == SSL_ERROR_SSL ) {
839 die (STATE_WARNING, _("HTTP WARNING - Client Certificate Required\n"));
841 die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
847 die (STATE_CRITICAL
, _("HTTP CRITICAL - Error on receive\n"));
855 /* return a CRITICAL status if we couldn't read any data */
856 if (pagesize
== (size_t) 0)
857 die (STATE_CRITICAL
, _("HTTP CRITICAL - No data received from host\n"));
859 /* close the connection */
861 np_net_ssl_cleanup();
865 /* reset the alarm */
868 /* leave full_page untouched so we can free it later */
872 printf ("%s://%s:%d%s is %d characters\n",
873 use_ssl
? "https" : "http", server_address
,
874 server_port
, server_url
, (int)pagesize
);
876 /* find status line and null-terminate it */
878 page
+= (size_t) strcspn (page
, "\r\n");
880 page
+= (size_t) strspn (page
, "\r\n");
881 status_line
[strcspn(status_line
, "\r\n")] = 0;
884 printf ("STATUS: %s\n", status_line
);
886 /* find header info and null-terminate it */
888 while (strcspn (page
, "\r\n") > 0) {
889 page
+= (size_t) strcspn (page
, "\r\n");
891 if ((strspn (page
, "\r") == 1 && strspn (page
, "\r\n") >= 2) ||
892 (strspn (page
, "\n") == 1 && strspn (page
, "\r\n") >= 2))
897 page
+= (size_t) strspn (page
, "\r\n");
898 header
[pos
- header
] = 0;
900 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header
,
901 (no_body
? " [[ skipped ]]" : page
));
903 /* make sure the status line matches the response we are looking for */
904 if (!strstr (status_line
, server_expect
)) {
905 if (server_port
== HTTP_PORT
)
907 _("Invalid HTTP response received from host\n"));
910 _("Invalid HTTP response received from host on port %d\n"),
912 die (STATE_CRITICAL
, "HTTP CRITICAL - %s", msg
);
915 /* Exit here if server_expect was set by user and not default */
916 if ( server_expect_yn
) {
918 _("HTTP OK: Status line output matched \"%s\"\n"),
924 /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
925 /* HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
926 /* Status-Code = 3 DIGITS */
928 status_code
= strchr (status_line
, ' ') + sizeof (char);
929 if (strspn (status_code
, "1234567890") != 3)
930 die (STATE_CRITICAL
, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line
);
932 http_status
= atoi (status_code
);
934 /* check the return code */
936 if (http_status
>= 600 || http_status
< 100)
937 die (STATE_CRITICAL
, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line
);
939 /* server errors result in a critical state */
940 else if (http_status
>= 500)
941 die (STATE_CRITICAL
, _("HTTP CRITICAL: %s\n"), status_line
);
943 /* client errors result in a warning state */
944 else if (http_status
>= 400)
945 die (STATE_WARNING
, _("HTTP WARNING: %s\n"), status_line
);
947 /* check redirected page if specified */
948 else if (http_status
>= 300) {
950 if (onredirect
== STATE_DEPENDENT
)
951 redir (header
, status_line
);
952 else if (onredirect
== STATE_UNKNOWN
)
953 printf (_("HTTP UNKNOWN"));
954 else if (onredirect
== STATE_OK
)
955 printf (_("HTTP OK"));
956 else if (onredirect
== STATE_WARNING
)
957 printf (_("HTTP WARNING"));
958 else if (onredirect
== STATE_CRITICAL
)
959 printf (_("HTTP CRITICAL"));
960 microsec
= deltime (tv
);
961 elapsed_time
= (double)microsec
/ 1.0e6
;
963 _(" - %s - %.3f second response time %s|%s %s\n"),
964 status_line
, elapsed_time
,
965 (display_html
? "</A>" : ""),
966 perfd_time (elapsed_time
), perfd_size (pagesize
));
967 } /* end if (http_status >= 300) */
969 } /* end else (server_expect_yn) */
971 if (maximum_age
>= 0) {
972 check_document_dates (header
);
975 /* check elapsed time */
976 microsec
= deltime (tv
);
977 elapsed_time
= (double)microsec
/ 1.0e6
;
979 _("HTTP WARNING: %s - %.3f second response time %s|%s %s\n"),
980 status_line
, elapsed_time
,
981 (display_html
? "</A>" : ""),
982 perfd_time (elapsed_time
), perfd_size (pagesize
));
983 if (check_critical_time
== TRUE
&& elapsed_time
> critical_time
)
984 die (STATE_CRITICAL
, "%s", msg
);
985 if (check_warning_time
== TRUE
&& elapsed_time
> warning_time
)
986 die (STATE_WARNING
, "%s", msg
);
988 /* Page and Header content checks go here */
989 /* these checks should be last */
991 if (strlen (string_expect
)) {
992 if (strstr (page
, string_expect
)) {
993 printf (_("HTTP OK %s - %.3f second response time %s|%s %s\n"),
994 status_line
, elapsed_time
,
995 (display_html
? "</A>" : ""),
996 perfd_time (elapsed_time
), perfd_size (pagesize
));
1000 printf (_("HTTP CRITICAL - string not found%s|%s %s\n"),
1001 (display_html
? "</A>" : ""),
1002 perfd_time (elapsed_time
), perfd_size (pagesize
));
1003 exit (STATE_CRITICAL
);
1007 if (strlen (regexp
)) {
1008 errcode
= regexec (&preg
, page
, REGS
, pmatch
, 0);
1009 if ((errcode
== 0 && invert_regex
== 0) || (errcode
== REG_NOMATCH
&& invert_regex
== 1)) {
1010 printf (_("HTTP OK %s - %.3f second response time %s|%s %s\n"),
1011 status_line
, elapsed_time
,
1012 (display_html
? "</A>" : ""),
1013 perfd_time (elapsed_time
), perfd_size (pagesize
));
1016 else if ((errcode
== REG_NOMATCH
&& invert_regex
== 0) || (errcode
== 0 && invert_regex
== 1)) {
1017 if (invert_regex
== 0)
1018 msg
= strdup(_("pattern not found"));
1020 msg
= strdup(_("pattern found"));
1021 printf (("%s - %s%s|%s %s\n"),
1024 (display_html
? "</A>" : ""),
1025 perfd_time (elapsed_time
), perfd_size (pagesize
));
1026 exit (STATE_CRITICAL
);
1029 regerror (errcode
, &preg
, errbuf
, MAX_INPUT_BUFFER
);
1030 printf (_("HTTP CRITICAL - Execute Error: %s\n"), errbuf
);
1031 exit (STATE_CRITICAL
);
1035 /* make sure the page is of an appropriate size */
1036 /* page_len = get_content_length(header); */
1037 page_len
= pagesize
;
1038 if ((max_page_len
> 0) && (page_len
> max_page_len
)) {
1039 printf (_("HTTP WARNING: page size %d too large%s|%s\n"),
1040 page_len
, (display_html
? "</A>" : ""), perfd_size (page_len
) );
1041 exit (STATE_WARNING
);
1042 } else if ((min_page_len
> 0) && (page_len
< min_page_len
)) {
1043 printf (_("HTTP WARNING: page size %d too small%s|%s\n"),
1044 page_len
, (display_html
? "</A>" : ""), perfd_size (page_len
) );
1045 exit (STATE_WARNING
);
1047 /* We only get here if all tests have been passed */
1048 asprintf (&msg
, _("HTTP OK %s - %d bytes in %.3f seconds %s|%s %s\n"),
1049 status_line
, page_len
, elapsed_time
,
1050 (display_html
? "</A>" : ""),
1051 perfd_time (elapsed_time
), perfd_size (page_len
));
1052 die (STATE_OK
, "%s", msg
);
1053 return STATE_UNKNOWN
;
1059 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
1060 #define URI_HTTP "%[HTPShtps]://"
1061 #define URI_HOST "%[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1062 #define URI_PORT ":%[0123456789]"
1063 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1064 #define HD1 URI_HTTP URI_HOST URI_PORT URI_PATH
1065 #define HD2 URI_HTTP URI_HOST URI_PATH
1066 #define HD3 URI_HTTP URI_HOST URI_PORT
1067 #define HD4 URI_HTTP URI_HOST
1068 #define HD5 URI_PATH
1071 redir (char *pos
, char *status_line
)
1081 addr
= malloc (MAX_IPV4_HOSTLENGTH
+ 1);
1083 die (STATE_UNKNOWN
, _("HTTP UNKNOWN - Could not allocate addr\n"));
1085 url
= malloc (strcspn (pos
, "\r\n"));
1087 die (STATE_UNKNOWN
, _("HTTP UNKNOWN - Could not allocate url\n"));
1090 sscanf (pos
, "%[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]:%n", xx
, &i
);
1092 pos
+= (size_t) strcspn (pos
, "\r\n");
1093 pos
+= (size_t) strspn (pos
, "\r\n");
1094 if (strlen(pos
) == 0)
1096 _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"),
1097 status_line
, (display_html
? "</A>" : ""));
1102 pos
+= strspn (pos
, " \t\r\n");
1104 url
= realloc (url
, strcspn (pos
, "\r\n"));
1106 die (STATE_UNKNOWN
, _("HTTP UNKNOWN - could not allocate url\n"));
1108 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
1109 if (sscanf (pos
, HD1
, type
, addr
, port
, url
) == 4) {
1110 use_ssl
= server_type_check (type
);
1114 /* URI_HTTP URI_HOST URI_PATH */
1115 else if (sscanf (pos
, HD2
, type
, addr
, url
) == 3 ) {
1116 use_ssl
= server_type_check (type
);
1117 i
= server_port_check (use_ssl
);
1120 /* URI_HTTP URI_HOST URI_PORT */
1121 else if(sscanf (pos
, HD3
, type
, addr
, port
) == 3) {
1122 strcpy (url
, HTTP_URL
);
1123 use_ssl
= server_type_check (type
);
1127 /* URI_HTTP URI_HOST */
1128 else if(sscanf (pos
, HD4
, type
, addr
) == 2) {
1129 strcpy (url
, HTTP_URL
);
1130 use_ssl
= server_type_check (type
);
1131 i
= server_port_check (use_ssl
);
1135 else if (sscanf (pos
, HD5
, url
) == 1) {
1137 if ((url
[0] != '/')) {
1138 if ((x
= strrchr(server_url
, '/')))
1140 asprintf (&url
, "%s/%s", server_url
, url
);
1143 strcpy (type
, server_type
);
1144 strcpy (addr
, host_name
);
1149 _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"),
1150 pos
, (display_html
? "</A>" : ""));
1155 } /* end while (pos) */
1157 if (++redir_depth
> max_depth
)
1159 _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
1160 max_depth
, type
, addr
, i
, url
, (display_html
? "</A>" : ""));
1162 if (server_port
==i
&&
1163 !strcmp(server_address
, addr
) &&
1164 (host_name
&& !strcmp(host_name
, addr
)) &&
1165 !strcmp(server_url
, url
))
1167 _("HTTP WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1168 type
, addr
, i
, url
, (display_html
? "</A>" : ""));
1171 strcpy (server_type
, type
);
1174 host_name
= strdup (addr
);
1176 free (server_address
);
1177 server_address
= strdup (addr
);
1180 server_url
= strdup (url
);
1188 server_type_check (const char *type
)
1190 if (strcmp (type
, "https"))
1197 server_port_check (int ssl_flag
)
1205 char *perfd_time (double elapsed_time
)
1207 return fperfdata ("time", elapsed_time
, "s",
1208 check_warning_time
, warning_time
,
1209 check_critical_time
, critical_time
,
1215 char *perfd_size (int page_len
)
1217 return perfdata ("size", page_len
, "B",
1218 (min_page_len
>0?TRUE
:FALSE
), min_page_len
,
1219 (min_page_len
>0?TRUE
:FALSE
), 0,
1226 print_revision (progname
, revision
);
1228 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1229 printf (COPYRIGHT
, copyright
, email
);
1231 printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
1232 printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
1233 printf ("%s\n", _("strings and regular expressions, check connection times, and report on"));
1234 printf ("%s\n", _("certificate expiration times."));
1240 printf (_("NOTE: One or both of -H and -I must be specified"));
1244 printf (_(UT_HELP_VRSN
));
1246 printf (" %s\n", "-H, --hostname=ADDRESS");
1247 printf (" %s\n", _("Host name argument for servers using host headers (virtual host)"));
1248 printf (" %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1249 printf (" %s\n", "-I, --IP-address=ADDRESS");
1250 printf (" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1251 printf (" %s\n", "-p, --port=INTEGER");
1252 printf (" %s", _("Port number (default: "));
1253 printf ("%d)\n", HTTP_PORT
);
1255 printf (_(UT_IPv46
));
1258 printf (" %s\n", "-S, --ssl");
1259 printf (" %s\n", _("Connect via SSL. Port defaults to 443"));
1260 printf (" %s\n", "-C, --certificate=INTEGER");
1261 printf (" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
1262 printf (" %s\n", _("(when this option is used the url is not checked.)\n"));
1265 printf (" %s\n", "-e, --expect=STRING");
1266 printf (" %s\n", _("String to expect in first (status) line of server response (default: "));
1267 printf ("%s)\n", HTTP_EXPECT
);
1268 printf (" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1269 printf (" %s\n", "-s, --string=STRING");
1270 printf (" %s\n", _("String to expect in the content"));
1271 printf (" %s\n", "-u, --url=PATH");
1272 printf (" %s\n", _("URL to GET or POST (default: /)"));
1273 printf (" %s\n", "-P, --post=STRING");
1274 printf (" %s\n", _("URL encoded http POST data"));
1275 printf (" %s\n", "-N, --no-body");
1276 printf (" %s\n", _("Don't wait for document body: stop reading after headers."));
1277 printf (" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
1278 printf (" %s\n", "-M, --max-age=SECONDS");
1279 printf (" %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
1280 printf (" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1281 printf (" %s\n", "-T, --content-type=STRING");
1282 printf (" %s\n", _("specify Content-Type header media type when POSTing\n"));
1284 printf (" %s\n", "-l, --linespan");
1285 printf (" %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1286 printf (" %s\n", "-r, --regex, --ereg=STRING");
1287 printf (" %s\n", _("Search page for regex STRING"));
1288 printf (" %s\n", "-R, --eregi=STRING");
1289 printf (" %s\n", _("Search page for case-insensitive regex STRING"));
1290 printf (" %s\n", "--invert-regex");
1291 printf (" %s\n", _("Return CRITICAL if found, OK if not\n"));
1293 printf (" %s\n", "-a, --authorization=AUTH_PAIR");
1294 printf (" %s\n", _("Username:password on sites with basic authentication"));
1295 printf (" %s\n", "-A, --useragent=STRING");
1296 printf (" %s\n", _("String to be sent in http header as \"User Agent\""));
1297 printf (" %s\n", "-k, --header=STRING");
1298 printf (" %s\n", _(" Any other tags to be sent in http header. Use multiple times for additional headers"));
1299 printf (" %s\n", "-L, --link");
1300 printf (" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1301 printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow>");
1302 printf (" %s\n", _("How to handle redirected pages"));
1303 printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1304 printf (" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1306 printf (_(UT_WARN_CRIT
));
1308 printf (_(UT_TIMEOUT
), DEFAULT_SOCKET_TIMEOUT
);
1310 printf (_(UT_VERBOSE
));
1312 printf (_("Notes:"));
1313 printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1314 printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1315 printf (" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect reponse"));
1316 printf (" %s\n", _("messages from the host result in STATE_WARNING return values. If you are"));
1317 printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1318 printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
1321 printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
1322 printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
1323 printf (" %s\n", _("certificate is still valid for the specified number of days."));
1324 printf (_("Examples:"));
1325 printf (" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com");
1326 printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
1327 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1328 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1329 printf (" %s\n\n", _("a STATE_CRITICAL will be returned."));
1331 printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 14");
1332 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1333 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1334 printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
1335 printf (" %s\n\n", _("the certificate is expired."));
1338 printf (_(UT_SUPPORT
));
1347 printf (_("Usage:"));
1348 printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname
);
1349 printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n");
1350 printf (" [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n");
1351 printf (" [-s string] [-l] [-r <regex> | -R <case-insensitive regex>] [-P string]\n");
1352 printf (" [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>] [-A string]\n");
1353 printf (" [-k string] [-S] [-C <age>] [-T <content-type>]\n");