1 /*****************************************************************************
3 * Nagios check_http plugin
6 * Copyright (c) 1999-2008 Nagios Plugins Development Team
10 * This file contains the check_http plugin
12 * This plugin tests the HTTP service on the specified host. It can test
13 * normal (http) and secure (https) servers, follow redirects, search for
14 * strings and regular expressions, check connection times, and report on
15 * certificate expiration times.
18 * This program is free software: you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation, either version 3 of the License, or
21 * (at your option) any later version.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
28 * You should have received a copy of the GNU General Public License
29 * along with this program. If not, see <http://www.gnu.org/licenses/>.
32 *****************************************************************************/
34 /* splint -I. -I../../plugins -I../../lib/ -I/usr/kerberos/include/ ../../plugins/check_http.c */
36 const char *progname
= "check_http";
37 const char *copyright
= "1999-2008";
38 const char *email
= "nagiosplug-devel@lists.sourceforge.net";
46 #define INPUT_DELIMITER ";"
51 #define HTTP_EXPECT "HTTP/1."
53 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
;
112 int followsticky
= STICKY_NONE
;
116 int min_page_len
= 0;
117 int max_page_len
= 0;
121 char *http_post_data
;
122 char *http_content_type
;
123 char buffer
[MAX_INPUT_BUFFER
];
125 int process_arguments (int, char **);
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 setlocale (LC_ALL
, "");
141 bindtextdomain (PACKAGE
, LOCALEDIR
);
142 textdomain (PACKAGE
);
144 /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
145 server_url
= strdup(HTTP_URL
);
146 server_url_length
= strlen(server_url
);
147 asprintf (&user_agent
, "User-Agent: check_http/v%s (nagios-plugins %s)",
148 NP_VERSION
, VERSION
);
150 /* Parse extra opts if any */
151 argv
=np_extra_opts (&argc
, argv
, progname
);
153 if (process_arguments (argc
, argv
) == ERROR
)
154 usage4 (_("Could not parse arguments"));
156 if (display_html
== TRUE
)
157 printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">",
158 use_ssl
? "https" : "http", host_name
? host_name
: server_address
,
159 server_port
, server_url
);
161 /* initialize alarm signal handling, set socket timeout, start timer */
162 (void) signal (SIGALRM
, socket_timeout_alarm_handler
);
163 (void) alarm (socket_timeout
);
164 gettimeofday (&tv
, NULL
);
166 result
= check_http ();
172 /* process command-line arguments */
174 process_arguments (int argc
, char **argv
)
180 INVERT_REGEX
= CHAR_MAX
+ 1
184 static struct option longopts
[] = {
186 {"link", no_argument
, 0, 'L'},
187 {"nohtml", no_argument
, 0, 'n'},
188 {"ssl", no_argument
, 0, 'S'},
189 {"post", required_argument
, 0, 'P'},
190 {"method", required_argument
, 0, 'j'},
191 {"IP-address", required_argument
, 0, 'I'},
192 {"url", required_argument
, 0, 'u'},
193 {"port", required_argument
, 0, 'p'},
194 {"authorization", required_argument
, 0, 'a'},
195 {"string", required_argument
, 0, 's'},
196 {"expect", required_argument
, 0, 'e'},
197 {"regex", required_argument
, 0, 'r'},
198 {"ereg", required_argument
, 0, 'r'},
199 {"eregi", required_argument
, 0, 'R'},
200 {"linespan", no_argument
, 0, 'l'},
201 {"onredirect", required_argument
, 0, 'f'},
202 {"certificate", required_argument
, 0, 'C'},
203 {"useragent", required_argument
, 0, 'A'},
204 {"header", required_argument
, 0, 'k'},
205 {"no-body", no_argument
, 0, 'N'},
206 {"max-age", required_argument
, 0, 'M'},
207 {"content-type", required_argument
, 0, 'T'},
208 {"pagesize", required_argument
, 0, 'm'},
209 {"invert-regex", no_argument
, NULL
, INVERT_REGEX
},
210 {"use-ipv4", no_argument
, 0, '4'},
211 {"use-ipv6", no_argument
, 0, '6'},
218 for (c
= 1; c
< argc
; c
++) {
219 if (strcmp ("-to", argv
[c
]) == 0)
220 strcpy (argv
[c
], "-t");
221 if (strcmp ("-hn", argv
[c
]) == 0)
222 strcpy (argv
[c
], "-H");
223 if (strcmp ("-wt", argv
[c
]) == 0)
224 strcpy (argv
[c
], "-w");
225 if (strcmp ("-ct", argv
[c
]) == 0)
226 strcpy (argv
[c
], "-c");
227 if (strcmp ("-nohtml", argv
[c
]) == 0)
228 strcpy (argv
[c
], "-n");
232 c
= getopt_long (argc
, argv
, "Vvh46t:c:w:A:k:H:P:j:T:I:a:e:p:s:R:r:u:f:C:nlLSm:M:N", longopts
, &option
);
233 if (c
== -1 || c
== EOF
)
237 case '?': /* usage */
244 case 'V': /* version */
245 print_revision (progname
, NP_VERSION
);
248 case 't': /* timeout period */
249 if (!is_intnonneg (optarg
))
250 usage2 (_("Timeout interval must be a positive integer"), optarg
);
252 socket_timeout
= atoi (optarg
);
254 case 'c': /* critical time threshold */
255 if (!is_nonnegative (optarg
))
256 usage2 (_("Critical threshold must be integer"), optarg
);
258 critical_time
= strtod (optarg
, NULL
);
259 check_critical_time
= TRUE
;
262 case 'w': /* warning time threshold */
263 if (!is_nonnegative (optarg
))
264 usage2 (_("Warning threshold must be integer"), optarg
);
266 warning_time
= strtod (optarg
, NULL
);
267 check_warning_time
= TRUE
;
270 case 'A': /* User Agent String */
271 asprintf (&user_agent
, "User-Agent: %s", optarg
);
273 case 'k': /* Additional headers */
274 if (http_opt_headers_count
== 0)
275 http_opt_headers
= malloc (sizeof (char *) * (++http_opt_headers_count
));
277 http_opt_headers
= realloc (http_opt_headers
, sizeof (char *) * (++http_opt_headers_count
));
278 http_opt_headers
[http_opt_headers_count
- 1] = optarg
;
279 /* asprintf (&http_opt_headers, "%s", optarg); */
281 case 'L': /* show html link */
284 case 'n': /* do not show html link */
285 display_html
= FALSE
;
287 case 'C': /* Check SSL cert validity */
289 if (!is_intnonneg (optarg
))
290 usage2 (_("Invalid certificate expiration period"), optarg
);
292 days_till_exp
= atoi (optarg
);
295 /* Fall through to -S option */
297 case 'S': /* use SSL */
299 usage4 (_("Invalid option - SSL is not available"));
302 if (specify_port
== FALSE
)
303 server_port
= HTTPS_PORT
;
305 case 'f': /* onredirect */
306 if (!strcmp (optarg
, "stickyport"))
307 onredirect
= STATE_DEPENDENT
, followsticky
= STICKY_HOST
|STICKY_PORT
;
308 else if (!strcmp (optarg
, "sticky"))
309 onredirect
= STATE_DEPENDENT
, followsticky
= STICKY_HOST
;
310 else if (!strcmp (optarg
, "follow"))
311 onredirect
= STATE_DEPENDENT
, followsticky
= STICKY_NONE
;
312 else if (!strcmp (optarg
, "unknown"))
313 onredirect
= STATE_UNKNOWN
;
314 else if (!strcmp (optarg
, "ok"))
315 onredirect
= STATE_OK
;
316 else if (!strcmp (optarg
, "warning"))
317 onredirect
= STATE_WARNING
;
318 else if (!strcmp (optarg
, "critical"))
319 onredirect
= STATE_CRITICAL
;
320 else usage2 (_("Invalid onredirect option"), optarg
);
322 printf(_("option f:%d \n"), onredirect
);
324 /* Note: H, I, and u must be malloc'd or will fail on redirects */
325 case 'H': /* Host Name (virtual host) */
326 host_name
= strdup (optarg
);
327 if (host_name
[0] == '[') {
328 if ((p
= strstr (host_name
, "]:")) != NULL
) /* [IPv6]:port */
329 server_port
= atoi (p
+ 2);
330 } else if ((p
= strchr (host_name
, ':')) != NULL
331 && strchr (++p
, ':') == NULL
) /* IPv4:port or host:port */
332 server_port
= atoi (p
);
334 case 'I': /* Server IP-address */
335 server_address
= strdup (optarg
);
337 case 'u': /* URL path */
338 server_url
= strdup (optarg
);
339 server_url_length
= strlen (server_url
);
341 case 'p': /* Server port */
342 if (!is_intnonneg (optarg
))
343 usage2 (_("Invalid port number"), optarg
);
345 server_port
= atoi (optarg
);
349 case 'a': /* authorization info */
350 strncpy (user_auth
, optarg
, MAX_INPUT_BUFFER
- 1);
351 user_auth
[MAX_INPUT_BUFFER
- 1] = 0;
353 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
354 if (! http_post_data
)
355 http_post_data
= strdup (optarg
);
357 http_method
= strdup("POST");
359 case 'j': /* Set HTTP method */
362 http_method
= strdup (optarg
);
364 case 's': /* string or substring */
365 strncpy (string_expect
, optarg
, MAX_INPUT_BUFFER
- 1);
366 string_expect
[MAX_INPUT_BUFFER
- 1] = 0;
368 case 'e': /* string or substring */
369 strncpy (server_expect
, optarg
, MAX_INPUT_BUFFER
- 1);
370 server_expect
[MAX_INPUT_BUFFER
- 1] = 0;
371 server_expect_yn
= 1;
373 case 'T': /* Content-type */
374 asprintf (&http_content_type
, "%s", optarg
);
376 case 'l': /* linespan */
377 cflags
&= ~REG_NEWLINE
;
379 case 'R': /* regex */
381 case 'r': /* regex */
382 strncpy (regexp
, optarg
, MAX_RE_SIZE
- 1);
383 regexp
[MAX_RE_SIZE
- 1] = 0;
384 errcode
= regcomp (&preg
, regexp
, cflags
);
386 (void) regerror (errcode
, &preg
, errbuf
, MAX_INPUT_BUFFER
);
387 printf (_("Could Not Compile Regular Expression: %s"), errbuf
);
395 address_family
= AF_INET
;
399 address_family
= AF_INET6
;
401 usage4 (_("IPv6 support not available"));
404 case 'v': /* verbose */
407 case 'm': /* min_page_length */
410 if (strchr(optarg
, ':') != (char *)NULL
) {
411 /* range, so get two values, min:max */
412 tmp
= strtok(optarg
, ":");
414 printf("Bad format: try \"-m min:max\"\n");
415 exit (STATE_WARNING
);
417 min_page_len
= atoi(tmp
);
419 tmp
= strtok(NULL
, ":");
421 printf("Bad format: try \"-m min:max\"\n");
422 exit (STATE_WARNING
);
424 max_page_len
= atoi(tmp
);
426 min_page_len
= atoi (optarg
);
429 case 'N': /* no-body */
432 case 'M': /* max-age */
434 int L
= strlen(optarg
);
435 if (L
&& optarg
[L
-1] == 'm')
436 maximum_age
= atoi (optarg
) * 60;
437 else if (L
&& optarg
[L
-1] == 'h')
438 maximum_age
= atoi (optarg
) * 60 * 60;
439 else if (L
&& optarg
[L
-1] == 'd')
440 maximum_age
= atoi (optarg
) * 60 * 60 * 24;
441 else if (L
&& (optarg
[L
-1] == 's' ||
442 isdigit (optarg
[L
-1])))
443 maximum_age
= atoi (optarg
);
445 fprintf (stderr
, "unparsable max-age: %s\n", optarg
);
446 exit (STATE_WARNING
);
455 if (server_address
== NULL
&& c
< argc
)
456 server_address
= strdup (argv
[c
++]);
458 if (host_name
== NULL
&& c
< argc
)
459 host_name
= strdup (argv
[c
++]);
461 if (server_address
== NULL
) {
462 if (host_name
== NULL
)
463 usage4 (_("You must specify a server address or host name"));
465 server_address
= strdup (host_name
);
468 if (check_critical_time
&& critical_time
>(double)socket_timeout
)
469 socket_timeout
= (int)critical_time
+ 1;
471 if (http_method
== NULL
)
472 http_method
= strdup ("GET");
479 /* Returns 1 if we're done processing the document body; 0 to keep going */
481 document_headers_done (char *full_page
)
485 for (body
= full_page
; *body
; body
++) {
486 if (!strncmp (body
, "\n\n", 2) || !strncmp (body
, "\n\r\n", 3))
491 return 0; /* haven't read end of headers yet */
493 full_page
[body
- full_page
] = 0;
498 parse_time_string (const char *string
)
502 memset (&tm
, 0, sizeof(tm
));
504 /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */
506 if (isupper (string
[0]) && /* Tue */
507 islower (string
[1]) &&
508 islower (string
[2]) &&
511 (isdigit(string
[5]) || string
[5] == ' ') && /* 25 */
512 isdigit (string
[6]) &&
514 isupper (string
[8]) && /* Dec */
515 islower (string
[9]) &&
516 islower (string
[10]) &&
518 isdigit (string
[12]) && /* 2001 */
519 isdigit (string
[13]) &&
520 isdigit (string
[14]) &&
521 isdigit (string
[15]) &&
523 isdigit (string
[17]) && /* 02: */
524 isdigit (string
[18]) &&
526 isdigit (string
[20]) && /* 59: */
527 isdigit (string
[21]) &&
529 isdigit (string
[23]) && /* 03 */
530 isdigit (string
[24]) &&
532 'G' == string
[26] && /* GMT */
533 'M' == string
[27] && /* GMT */
536 tm
.tm_sec
= 10 * (string
[23]-'0') + (string
[24]-'0');
537 tm
.tm_min
= 10 * (string
[20]-'0') + (string
[21]-'0');
538 tm
.tm_hour
= 10 * (string
[17]-'0') + (string
[18]-'0');
539 tm
.tm_mday
= 10 * (string
[5] == ' ' ? 0 : string
[5]-'0') + (string
[6]-'0');
540 tm
.tm_mon
= (!strncmp (string
+8, "Jan", 3) ? 0 :
541 !strncmp (string
+8, "Feb", 3) ? 1 :
542 !strncmp (string
+8, "Mar", 3) ? 2 :
543 !strncmp (string
+8, "Apr", 3) ? 3 :
544 !strncmp (string
+8, "May", 3) ? 4 :
545 !strncmp (string
+8, "Jun", 3) ? 5 :
546 !strncmp (string
+8, "Jul", 3) ? 6 :
547 !strncmp (string
+8, "Aug", 3) ? 7 :
548 !strncmp (string
+8, "Sep", 3) ? 8 :
549 !strncmp (string
+8, "Oct", 3) ? 9 :
550 !strncmp (string
+8, "Nov", 3) ? 10 :
551 !strncmp (string
+8, "Dec", 3) ? 11 :
553 tm
.tm_year
= ((1000 * (string
[12]-'0') +
554 100 * (string
[13]-'0') +
555 10 * (string
[14]-'0') +
559 tm
.tm_isdst
= 0; /* GMT is never in DST, right? */
561 if (tm
.tm_mon
< 0 || tm
.tm_mday
< 1 || tm
.tm_mday
> 31)
565 This is actually wrong: we need to subtract the local timezone
566 offset from GMT from this value. But, that's ok in this usage,
567 because we only comparing these two GMT dates against each other,
568 so it doesn't matter what time zone we parse them in.
572 if (t
== (time_t) -1) t
= 0;
575 const char *s
= string
;
576 while (*s
&& *s
!= '\r' && *s
!= '\n')
577 fputc (*s
++, stdout
);
578 printf (" ==> %lu\n", (unsigned long) t
);
588 /* Checks if the server 'reply' is one of the expected 'statuscodes' */
590 expected_statuscode (const char *reply
, const char *statuscodes
)
592 char *expected
, *code
;
595 if ((expected
= strdup (statuscodes
)) == NULL
)
596 die (STATE_UNKNOWN
, _("HTTP UNKNOWN - Memory allocation error\n"));
598 for (code
= strtok (expected
, ","); code
!= NULL
; code
= strtok (NULL
, ","))
599 if (strstr (reply
, code
) != NULL
) {
609 check_document_dates (const char *headers
, char **msg
)
612 char *server_date
= 0;
613 char *document_date
= 0;
614 int date_result
= STATE_OK
;
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')))
633 /* Avoid stepping over end-of-string marker */
637 /* Process this header. */
638 if (value
&& value
> field
+2) {
639 char *ff
= (char *) malloc (value
-field
);
641 while (field
< value
-1)
642 *ss
++ = tolower(*field
++);
645 if (!strcmp (ff
, "date") || !strcmp (ff
, "last-modified")) {
647 while (*value
&& isspace (*value
))
649 for (e
= value
; *e
&& *e
!= '\r' && *e
!= '\n'; e
++)
651 ss
= (char *) malloc (e
- value
+ 1);
652 strncpy (ss
, value
, e
- value
);
654 if (!strcmp (ff
, "date")) {
655 if (server_date
) free (server_date
);
658 if (document_date
) free (document_date
);
666 /* Done parsing the body. Now check the dates we (hopefully) parsed. */
667 if (!server_date
|| !*server_date
) {
668 asprintf (msg
, _("%sServer date unknown, "), *msg
);
669 date_result
= max_state_alt(STATE_UNKNOWN
, date_result
);
670 } else if (!document_date
|| !*document_date
) {
671 asprintf (msg
, _("%sDocument modification date unknown, "), *msg
);
672 date_result
= max_state_alt(STATE_CRITICAL
, date_result
);
674 time_t srv_data
= parse_time_string (server_date
);
675 time_t doc_data
= parse_time_string (document_date
);
678 asprintf (msg
, _("%sServer date \"%100s\" unparsable, "), *msg
, server_date
);
679 date_result
= max_state_alt(STATE_CRITICAL
, date_result
);
680 } else if (doc_data
<= 0) {
681 asprintf (msg
, _("%sDocument date \"%100s\" unparsable, "), *msg
, document_date
);
682 date_result
= max_state_alt(STATE_CRITICAL
, date_result
);
683 } else if (doc_data
> srv_data
+ 30) {
684 asprintf (msg
, _("%sDocument is %d seconds in the future, "), *msg
, (int)doc_data
- (int)srv_data
);
685 date_result
= max_state_alt(STATE_CRITICAL
, date_result
);
686 } else if (doc_data
< srv_data
- maximum_age
) {
687 int n
= (srv_data
- doc_data
);
688 if (n
> (60 * 60 * 24 * 2)) {
689 asprintf (msg
, _("%sLast modified %.1f days ago, "), *msg
, ((float) n
) / (60 * 60 * 24));
690 date_result
= max_state_alt(STATE_CRITICAL
, date_result
);
692 asprintf (msg
, _("%sLast modified %d:%02d:%02d ago, "), *msg
, n
/ (60 * 60), (n
/ 60) % 60, n
% 60);
693 date_result
= max_state_alt(STATE_CRITICAL
, date_result
);
697 free (document_date
);
703 get_content_length (const char *headers
)
706 int content_length
= 0;
710 const char *field
= s
;
711 const char *value
= 0;
713 /* Find the end of the header field */
714 while (*s
&& !isspace(*s
) && *s
!= ':')
717 /* Remember the header value, if any. */
721 /* Skip to the end of the header, including continuation lines. */
722 while (*s
&& !(*s
== '\n' && (s
[1] != ' ' && s
[1] != '\t')))
726 /* Process this header. */
727 if (value
&& value
> field
+2) {
728 char *ff
= (char *) malloc (value
-field
);
730 while (field
< value
-1)
731 *ss
++ = tolower(*field
++);
734 if (!strcmp (ff
, "content-length")) {
736 while (*value
&& isspace (*value
))
738 for (e
= value
; *e
&& *e
!= '\r' && *e
!= '\n'; e
++)
740 ss
= (char *) malloc (e
- value
+ 1);
741 strncpy (ss
, value
, e
- value
);
743 content_length
= atoi(ss
);
749 return (content_length
);
753 prepend_slash (char *path
)
760 if ((newpath
= malloc (strlen(path
) + 2)) == NULL
)
761 die (STATE_UNKNOWN
, _("HTTP UNKNOWN - Memory allocation error\n"));
763 strcpy (newpath
+ 1, path
);
786 int result
= STATE_OK
;
788 /* try to connect to the host at the given port number */
789 if (my_tcp_connect (server_address
, server_port
, &sd
) != STATE_OK
)
790 die (STATE_CRITICAL
, _("HTTP CRITICAL - Unable to open TCP socket\n"));
792 if (use_ssl
== TRUE
) {
794 if (check_cert
== TRUE
) {
795 result
= np_net_ssl_check_cert(days_till_exp
);
796 np_net_ssl_cleanup();
801 #endif /* HAVE_SSL */
803 asprintf (&buf
, "%s %s %s\r\n%s\r\n", http_method
, server_url
, host_name
? "HTTP/1.1" : "HTTP/1.0", user_agent
);
805 /* tell HTTP/1.1 servers not to keep the connection alive */
806 asprintf (&buf
, "%sConnection: close\r\n", buf
);
808 /* optionally send the host header info */
811 * Specify the port only if we're using a non-default port (see RFC 2616,
812 * 14.23). Some server applications/configurations cause trouble if the
813 * (default) port is explicitly specified in the "Host:" header line.
815 if ((use_ssl
== FALSE
&& server_port
== HTTP_PORT
) ||
816 (use_ssl
== TRUE
&& server_port
== HTTPS_PORT
))
817 asprintf (&buf
, "%sHost: %s\r\n", buf
, host_name
);
819 asprintf (&buf
, "%sHost: %s:%d\r\n", buf
, host_name
, server_port
);
822 /* optionally send any other header tag */
823 if (http_opt_headers_count
) {
824 for (i
= 0; i
< http_opt_headers_count
; i
++) {
825 for ((pos
= strtok(http_opt_headers
[i
], INPUT_DELIMITER
)); pos
; (pos
= strtok(NULL
, INPUT_DELIMITER
)))
826 asprintf (&buf
, "%s%s\r\n", buf
, pos
);
828 /* This cannot be free'd here because a redirection will then try to access this and segfault */
829 /* Covered in a testcase in tests/check_http.t */
830 /* free(http_opt_headers); */
833 /* optionally send the authentication info */
834 if (strlen(user_auth
)) {
835 base64_encode_alloc (user_auth
, strlen (user_auth
), &auth
);
836 asprintf (&buf
, "%sAuthorization: Basic %s\r\n", buf
, auth
);
839 /* either send http POST data (any data, not only POST)*/
840 if (http_post_data
) {
841 if (http_content_type
) {
842 asprintf (&buf
, "%sContent-Type: %s\r\n", buf
, http_content_type
);
844 asprintf (&buf
, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf
);
847 asprintf (&buf
, "%sContent-Length: %i\r\n\r\n", buf
, (int)strlen (http_post_data
));
848 asprintf (&buf
, "%s%s%s", buf
, http_post_data
, CRLF
);
851 /* or just a newline so the server knows we're done with the request */
852 asprintf (&buf
, "%s%s", buf
, CRLF
);
855 if (verbose
) printf ("%s\n", buf
);
856 my_send (buf
, strlen (buf
));
859 full_page
= strdup("");
860 while ((i
= my_recv (buffer
, MAX_INPUT_BUFFER
-1)) > 0) {
862 asprintf (&full_page
, "%s%s", full_page
, buffer
);
865 if (no_body
&& document_headers_done (full_page
)) {
871 if (i
< 0 && errno
!= ECONNRESET
) {
875 sslerr=SSL_get_error(ssl, i);
876 if ( sslerr == SSL_ERROR_SSL ) {
877 die (STATE_WARNING, _("HTTP WARNING - Client Certificate Required\n"));
879 die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
885 die (STATE_CRITICAL
, _("HTTP CRITICAL - Error on receive\n"));
893 /* return a CRITICAL status if we couldn't read any data */
894 if (pagesize
== (size_t) 0)
895 die (STATE_CRITICAL
, _("HTTP CRITICAL - No data received from host\n"));
897 /* close the connection */
899 np_net_ssl_cleanup();
903 /* Save check time */
904 microsec
= deltime (tv
);
905 elapsed_time
= (double)microsec
/ 1.0e6
;
907 /* leave full_page untouched so we can free it later */
911 printf ("%s://%s:%d%s is %d characters\n",
912 use_ssl
? "https" : "http", server_address
,
913 server_port
, server_url
, (int)pagesize
);
915 /* find status line and null-terminate it */
917 page
+= (size_t) strcspn (page
, "\r\n");
919 page
+= (size_t) strspn (page
, "\r\n");
920 status_line
[strcspn(status_line
, "\r\n")] = 0;
923 printf ("STATUS: %s\n", status_line
);
925 /* find header info and null-terminate it */
927 while (strcspn (page
, "\r\n") > 0) {
928 page
+= (size_t) strcspn (page
, "\r\n");
930 if ((strspn (page
, "\r") == 1 && strspn (page
, "\r\n") >= 2) ||
931 (strspn (page
, "\n") == 1 && strspn (page
, "\r\n") >= 2))
936 page
+= (size_t) strspn (page
, "\r\n");
937 header
[pos
- header
] = 0;
939 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header
,
940 (no_body
? " [[ skipped ]]" : page
));
942 /* make sure the status line matches the response we are looking for */
943 if (!expected_statuscode (status_line
, server_expect
)) {
944 if (server_port
== HTTP_PORT
)
946 _("Invalid HTTP response received from host: %s\n"),
950 _("Invalid HTTP response received from host on port %d: %s\n"),
951 server_port
, status_line
);
952 die (STATE_CRITICAL
, "HTTP CRITICAL - %s", msg
);
955 /* Bypass normal status line check if server_expect was set by user and not default */
956 /* NOTE: After this if/else block msg *MUST* be an asprintf-allocated string */
957 if ( server_expect_yn
) {
959 _("Status line output matched \"%s\" - "), server_expect
);
964 /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
965 /* HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
966 /* Status-Code = 3 DIGITS */
968 status_code
= strchr (status_line
, ' ') + sizeof (char);
969 if (strspn (status_code
, "1234567890") != 3)
970 die (STATE_CRITICAL
, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line
);
972 http_status
= atoi (status_code
);
974 /* check the return code */
976 if (http_status
>= 600 || http_status
< 100) {
977 die (STATE_CRITICAL
, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line
);
979 /* server errors result in a critical state */
980 else if (http_status
>= 500) {
981 asprintf (&msg
, _("%s - "), status_line
);
982 result
= STATE_CRITICAL
;
984 /* client errors result in a warning state */
985 else if (http_status
>= 400) {
986 asprintf (&msg
, _("%s - "), status_line
);
987 result
= max_state_alt(STATE_WARNING
, result
);
989 /* check redirected page if specified */
990 else if (http_status
>= 300) {
992 if (onredirect
== STATE_DEPENDENT
)
993 redir (header
, status_line
);
995 result
= max_state_alt(onredirect
, result
);
996 asprintf (&msg
, _("%s - "), status_line
);
997 } /* end if (http_status >= 300) */
999 /* Print OK status anyway */
1000 asprintf (&msg
, _("%s - "), status_line
);
1003 } /* end else (server_expect_yn) */
1005 /* reset the alarm - must be called *after* redir or we'll never die on redirects! */
1008 if (maximum_age
>= 0) {
1009 result
= max_state_alt(check_document_dates(header
, &msg
), result
);
1012 /* Page and Header content checks go here */
1014 if (strlen (string_expect
)) {
1015 if (!strstr (page
, string_expect
)) {
1016 asprintf (&msg
, _("%sstring not found, "), msg
);
1017 result
= STATE_CRITICAL
;
1021 if (strlen (regexp
)) {
1022 errcode
= regexec (&preg
, page
, REGS
, pmatch
, 0);
1023 if ((errcode
== 0 && invert_regex
== 0) || (errcode
== REG_NOMATCH
&& invert_regex
== 1)) {
1024 /* OK - No-op to avoid changing the logic around it */
1025 result
= max_state_alt(STATE_OK
, result
);
1027 else if ((errcode
== REG_NOMATCH
&& invert_regex
== 0) || (errcode
== 0 && invert_regex
== 1)) {
1028 if (invert_regex
== 0)
1029 asprintf (&msg
, _("%spattern not found, "), msg
);
1031 asprintf (&msg
, _("%spattern found, "), msg
);
1032 result
= STATE_CRITICAL
;
1035 /* FIXME: Shouldn't that be UNKNOWN? */
1036 regerror (errcode
, &preg
, errbuf
, MAX_INPUT_BUFFER
);
1037 asprintf (&msg
, _("%sExecute Error: %s, "), msg
, errbuf
);
1038 result
= STATE_CRITICAL
;
1042 /* make sure the page is of an appropriate size */
1043 /* page_len = get_content_length(header); */
1044 /* FIXME: Will this work with -N ? IMHO we should use
1045 * get_content_length(header) and always check if it's different than the
1048 /* FIXME: IIRC pagesize returns headers - shouldn't we make
1049 * it == get_content_length(header) ??
1051 page_len
= pagesize
;
1052 if ((max_page_len
> 0) && (page_len
> max_page_len
)) {
1053 asprintf (&msg
, _("%spage size %d too large, "), msg
, page_len
);
1054 result
= max_state_alt(STATE_WARNING
, result
);
1055 } else if ((min_page_len
> 0) && (page_len
< min_page_len
)) {
1056 asprintf (&msg
, _("%spage size %d too small, "), msg
, page_len
);
1057 result
= max_state_alt(STATE_WARNING
, result
);
1060 /* Cut-off trailing characters */
1061 if(msg
[strlen(msg
)-2] == ',')
1062 msg
[strlen(msg
)-2] = '\0';
1064 msg
[strlen(msg
)-3] = '\0';
1066 /* check elapsed time */
1068 _("%s - %d bytes in %.3f second response time %s|%s %s"),
1069 msg
, page_len
, elapsed_time
,
1070 (display_html
? "</A>" : ""),
1071 perfd_time (elapsed_time
), perfd_size (page_len
));
1073 if (check_critical_time
== TRUE
&& elapsed_time
> critical_time
)
1074 result
= STATE_CRITICAL
;
1075 if (check_warning_time
== TRUE
&& elapsed_time
> warning_time
)
1076 result
= max_state_alt(STATE_WARNING
, result
);
1078 die (result
, "HTTP %s: %s\n", state_text(result
), msg
);
1080 return STATE_UNKNOWN
;
1086 #define URI_HTTP "%5[HTPShtps]"
1087 #define URI_HOST "%255[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1088 #define URI_PORT "%6d" /* MAX_PORT's width is 5 chars, 6 to detect overflow */
1089 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1090 #define HD1 URI_HTTP "://" URI_HOST ":" URI_PORT "/" URI_PATH
1091 #define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH
1092 #define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT
1093 #define HD4 URI_HTTP "://" URI_HOST
1094 #define HD5 URI_PATH
1097 redir (char *pos
, char *status_line
)
1106 addr
= malloc (MAX_IPV4_HOSTLENGTH
+ 1);
1108 die (STATE_UNKNOWN
, _("HTTP UNKNOWN - Could not allocate addr\n"));
1110 url
= malloc (strcspn (pos
, "\r\n"));
1112 die (STATE_UNKNOWN
, _("HTTP UNKNOWN - Could not allocate URL\n"));
1115 sscanf (pos
, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx
, &i
);
1117 pos
+= (size_t) strcspn (pos
, "\r\n");
1118 pos
+= (size_t) strspn (pos
, "\r\n");
1119 if (strlen(pos
) == 0)
1121 _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"),
1122 status_line
, (display_html
? "</A>" : ""));
1127 pos
+= strspn (pos
, " \t");
1130 * RFC 2616 (4.2): ``Header fields can be extended over multiple lines by
1131 * preceding each extra line with at least one SP or HT.''
1133 for (; (i
= strspn (pos
, "\r\n")); pos
+= i
) {
1135 if (!(i
= strspn (pos
, " \t"))) {
1136 die (STATE_UNKNOWN
, _("HTTP UNKNOWN - Empty redirect location%s\n"),
1137 display_html
? "</A>" : "");
1141 url
= realloc (url
, strcspn (pos
, "\r\n") + 1);
1143 die (STATE_UNKNOWN
, _("HTTP UNKNOWN - Could not allocate URL\n"));
1145 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
1146 if (sscanf (pos
, HD1
, type
, addr
, &i
, url
) == 4) {
1147 url
= prepend_slash (url
);
1148 use_ssl
= server_type_check (type
);
1151 /* URI_HTTP URI_HOST URI_PATH */
1152 else if (sscanf (pos
, HD2
, type
, addr
, url
) == 3 ) {
1153 url
= prepend_slash (url
);
1154 use_ssl
= server_type_check (type
);
1155 i
= server_port_check (use_ssl
);
1158 /* URI_HTTP URI_HOST URI_PORT */
1159 else if (sscanf (pos
, HD3
, type
, addr
, &i
) == 3) {
1160 strcpy (url
, HTTP_URL
);
1161 use_ssl
= server_type_check (type
);
1164 /* URI_HTTP URI_HOST */
1165 else if (sscanf (pos
, HD4
, type
, addr
) == 2) {
1166 strcpy (url
, HTTP_URL
);
1167 use_ssl
= server_type_check (type
);
1168 i
= server_port_check (use_ssl
);
1172 else if (sscanf (pos
, HD5
, url
) == 1) {
1174 if ((url
[0] != '/')) {
1175 if ((x
= strrchr(server_url
, '/')))
1177 asprintf (&url
, "%s/%s", server_url
, url
);
1180 strcpy (type
, server_type
);
1181 strcpy (addr
, host_name
? host_name
: server_address
);
1186 _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"),
1187 pos
, (display_html
? "</A>" : ""));
1192 } /* end while (pos) */
1194 if (++redir_depth
> max_depth
)
1196 _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
1197 max_depth
, type
, addr
, i
, url
, (display_html
? "</A>" : ""));
1199 if (server_port
==i
&&
1200 !strcmp(server_address
, addr
) &&
1201 (host_name
&& !strcmp(host_name
, addr
)) &&
1202 !strcmp(server_url
, url
))
1204 _("HTTP WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1205 type
, addr
, i
, url
, (display_html
? "</A>" : ""));
1207 strcpy (server_type
, type
);
1210 host_name
= strdup (addr
);
1212 if (!(followsticky
& STICKY_HOST
)) {
1213 free (server_address
);
1214 server_address
= strdup (addr
);
1216 if (!(followsticky
& STICKY_PORT
)) {
1223 if (server_port
> MAX_PORT
)
1225 _("HTTP UNKNOWN - Redirection to port above %d - %s://%s:%d%s%s\n"),
1226 MAX_PORT
, server_type
, server_address
, server_port
, server_url
,
1227 display_html
? "</A>" : "");
1230 printf (_("Redirection to %s://%s:%d%s\n"), server_type
,
1231 host_name
? host_name
: server_address
, server_port
, server_url
);
1238 server_type_check (const char *type
)
1240 if (strcmp (type
, "https"))
1247 server_port_check (int ssl_flag
)
1255 char *perfd_time (double elapsed_time
)
1257 return fperfdata ("time", elapsed_time
, "s",
1258 check_warning_time
, warning_time
,
1259 check_critical_time
, critical_time
,
1265 char *perfd_size (int page_len
)
1267 return perfdata ("size", page_len
, "B",
1268 (min_page_len
>0?TRUE
:FALSE
), min_page_len
,
1269 (min_page_len
>0?TRUE
:FALSE
), 0,
1276 print_revision (progname
, NP_VERSION
);
1278 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1279 printf (COPYRIGHT
, copyright
, email
);
1281 printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
1282 printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
1283 printf ("%s\n", _("strings and regular expressions, check connection times, and report on"));
1284 printf ("%s\n", _("certificate expiration times."));
1290 printf (_("NOTE: One or both of -H and -I must be specified"));
1294 printf (_(UT_HELP_VRSN
));
1295 printf (_(UT_EXTRA_OPTS
));
1297 printf (" %s\n", "-H, --hostname=ADDRESS");
1298 printf (" %s\n", _("Host name argument for servers using host headers (virtual host)"));
1299 printf (" %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1300 printf (" %s\n", "-I, --IP-address=ADDRESS");
1301 printf (" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1302 printf (" %s\n", "-p, --port=INTEGER");
1303 printf (" %s", _("Port number (default: "));
1304 printf ("%d)\n", HTTP_PORT
);
1306 printf (_(UT_IPv46
));
1309 printf (" %s\n", "-S, --ssl");
1310 printf (" %s\n", _("Connect via SSL. Port defaults to 443"));
1311 printf (" %s\n", "-C, --certificate=INTEGER");
1312 printf (" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
1313 printf (" %s\n", _("(when this option is used the URL is not checked.)\n"));
1316 printf (" %s\n", "-e, --expect=STRING");
1317 printf (" %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1318 printf (" %s", _("the first (status) line of the server response (default: "));
1319 printf ("%s)\n", HTTP_EXPECT
);
1320 printf (" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1321 printf (" %s\n", "-s, --string=STRING");
1322 printf (" %s\n", _("String to expect in the content"));
1323 printf (" %s\n", "-u, --url=PATH");
1324 printf (" %s\n", _("URL to GET or POST (default: /)"));
1325 printf (" %s\n", "-P, --post=STRING");
1326 printf (" %s\n", _("URL encoded http POST data"));
1327 printf (" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE)");
1328 printf (" %s\n", _("Set HTTP method."));
1329 printf (" %s\n", "-N, --no-body");
1330 printf (" %s\n", _("Don't wait for document body: stop reading after headers."));
1331 printf (" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
1332 printf (" %s\n", "-M, --max-age=SECONDS");
1333 printf (" %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
1334 printf (" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1335 printf (" %s\n", "-T, --content-type=STRING");
1336 printf (" %s\n", _("specify Content-Type header media type when POSTing\n"));
1338 printf (" %s\n", "-l, --linespan");
1339 printf (" %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1340 printf (" %s\n", "-r, --regex, --ereg=STRING");
1341 printf (" %s\n", _("Search page for regex STRING"));
1342 printf (" %s\n", "-R, --eregi=STRING");
1343 printf (" %s\n", _("Search page for case-insensitive regex STRING"));
1344 printf (" %s\n", "--invert-regex");
1345 printf (" %s\n", _("Return CRITICAL if found, OK if not\n"));
1347 printf (" %s\n", "-a, --authorization=AUTH_PAIR");
1348 printf (" %s\n", _("Username:password on sites with basic authentication"));
1349 printf (" %s\n", "-A, --useragent=STRING");
1350 printf (" %s\n", _("String to be sent in http header as \"User Agent\""));
1351 printf (" %s\n", "-k, --header=STRING");
1352 printf (" %s\n", _(" Any other tags to be sent in http header. Use multiple times for additional headers"));
1353 printf (" %s\n", "-L, --link");
1354 printf (" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1355 printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>");
1356 printf (" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1357 printf (" %s\n", _("specified IP address. stickyport also ensure post stays the same."));
1358 printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1359 printf (" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1361 printf (_(UT_WARN_CRIT
));
1363 printf (_(UT_TIMEOUT
), DEFAULT_SOCKET_TIMEOUT
);
1365 printf (_(UT_VERBOSE
));
1368 printf ("%s\n", _("Notes:"));
1369 printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1370 printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1371 printf (" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect reponse"));
1372 printf (" %s\n", _("messages from the host result in STATE_WARNING return values. If you are"));
1373 printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1374 printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
1376 printf (_(UT_EXTRA_OPTS_NOTES
));
1380 printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
1381 printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
1382 printf (" %s\n", _("certificate is still valid for the specified number of days."));
1384 printf ("%s\n", _("Examples:"));
1385 printf (" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com");
1386 printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
1387 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1388 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1389 printf (" %s\n\n", _("a STATE_CRITICAL will be returned."));
1391 printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 14");
1392 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1393 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1394 printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
1395 printf (" %s\n", _("the certificate is expired."));
1398 printf (_(UT_SUPPORT
));
1407 printf (_("Usage:"));
1408 printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname
);
1409 printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n");
1410 printf (" [-a auth] [-f <ok | warn | critcal | follow | sticky | stickyport>]\n");
1411 printf (" [-e <expect>] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
1412 printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
1413 printf (" [-A string] [-k string] [-S] [-C <age>] [-T <content-type>] [-j method]\n");