1 /******************************************************************************
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2 of the License, or
6 (at your option) any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 $Id$
19 ******************************************************************************/
20 /* splint -I. -I../../plugins -I../../lib/ -I/usr/kerberos/include/ ../../plugins/check_http.c */
22 const char *progname = "check_http";
23 const char *revision = "$Revision$";
24 const char *copyright = "1999-2005";
25 const char *email = "nagiosplug-devel@lists.sourceforge.net";
27 #include "common.h"
28 #include "netutils.h"
29 #include "utils.h"
31 #define INPUT_DELIMITER ";"
33 #define HTTP_EXPECT "HTTP/1."
34 enum {
35 MAX_IPV4_HOSTLENGTH = 255,
36 HTTP_PORT = 80,
37 HTTPS_PORT = 443
38 };
40 #ifdef HAVE_SSL
41 int check_cert = FALSE;
42 int days_till_exp;
43 char *randbuff;
44 X509 *server_cert;
45 # define my_recv(buf, len) ((use_ssl) ? np_net_ssl_read(buf, len) : read(sd, buf, len))
46 # define my_send(buf, len) ((use_ssl) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0))
47 #else /* ifndef HAVE_SSL */
48 # define my_recv(buf, len) read(sd, buf, len)
49 # define my_send(buf, len) send(sd, buf, len, 0)
50 #endif /* HAVE_SSL */
51 int no_body = FALSE;
52 int maximum_age = -1;
54 enum {
55 REGS = 2,
56 MAX_RE_SIZE = 256
57 };
58 #include "regex.h"
59 regex_t preg;
60 regmatch_t pmatch[REGS];
61 char regexp[MAX_RE_SIZE];
62 char errbuf[MAX_INPUT_BUFFER];
63 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
64 int errcode;
65 int invert_regex = 0;
67 struct timeval tv;
69 #define HTTP_URL "/"
70 #define CRLF "\r\n"
72 char timestamp[17] = "";
73 int specify_port = FALSE;
74 int server_port = HTTP_PORT;
75 char server_port_text[6] = "";
76 char server_type[6] = "http";
77 char *server_address;
78 char *host_name;
79 char *server_url;
80 char *user_agent;
81 int server_url_length;
82 int server_expect_yn = 0;
83 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
84 char string_expect[MAX_INPUT_BUFFER] = "";
85 double warning_time = 0;
86 int check_warning_time = FALSE;
87 double critical_time = 0;
88 int check_critical_time = FALSE;
89 char user_auth[MAX_INPUT_BUFFER] = "";
90 int display_html = FALSE;
91 char **http_opt_headers;
92 int http_opt_headers_count = 0;
93 int onredirect = STATE_OK;
94 int use_ssl = FALSE;
95 int verbose = FALSE;
96 int sd;
97 int min_page_len = 0;
98 int max_page_len = 0;
99 int redir_depth = 0;
100 int max_depth = 15;
101 char *http_method;
102 char *http_post_data;
103 char *http_content_type;
104 char buffer[MAX_INPUT_BUFFER];
106 int process_arguments (int, char **);
107 static char *base64 (const char *bin, size_t len);
108 int check_http (void);
109 void redir (char *pos, char *status_line);
110 int server_type_check(const char *type);
111 int server_port_check(int ssl_flag);
112 char *perfd_time (double microsec);
113 char *perfd_size (int page_len);
114 void print_help (void);
115 void print_usage (void);
117 int
118 main (int argc, char **argv)
119 {
120 int result = STATE_UNKNOWN;
122 /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
123 server_url = strdup(HTTP_URL);
124 server_url_length = strlen(server_url);
125 asprintf (&user_agent, "User-Agent: check_http/%s (nagios-plugins %s)",
126 clean_revstring (revision), VERSION);
128 if (process_arguments (argc, argv) == ERROR)
129 usage4 (_("Could not parse arguments"));
131 if (strstr (timestamp, ":")) {
132 if (strstr (server_url, "?"))
133 asprintf (&server_url, "%s&%s", server_url, timestamp);
134 else
135 asprintf (&server_url, "%s?%s", server_url, timestamp);
136 }
138 if (display_html == TRUE)
139 printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">",
140 use_ssl ? "https" : "http", host_name,
141 server_port, server_url);
143 /* initialize alarm signal handling, set socket timeout, start timer */
144 (void) signal (SIGALRM, socket_timeout_alarm_handler);
145 (void) alarm (socket_timeout);
146 gettimeofday (&tv, NULL);
148 result = check_http ();
149 return result;
150 }
154 /* process command-line arguments */
155 int
156 process_arguments (int argc, char **argv)
157 {
158 int c = 1;
160 enum {
161 INVERT_REGEX = CHAR_MAX + 1
162 };
164 int option = 0;
165 static struct option longopts[] = {
166 STD_LONG_OPTS,
167 {"file",required_argument,0,'F'},
168 {"link", no_argument, 0, 'L'},
169 {"nohtml", no_argument, 0, 'n'},
170 {"ssl", no_argument, 0, 'S'},
171 {"verbose", no_argument, 0, 'v'},
172 {"post", required_argument, 0, 'P'},
173 {"IP-address", required_argument, 0, 'I'},
174 {"url", required_argument, 0, 'u'},
175 {"string", required_argument, 0, 's'},
176 {"regex", required_argument, 0, 'r'},
177 {"ereg", required_argument, 0, 'r'},
178 {"eregi", required_argument, 0, 'R'},
179 {"linespan", no_argument, 0, 'l'},
180 {"onredirect", required_argument, 0, 'f'},
181 {"certificate", required_argument, 0, 'C'},
182 {"useragent", required_argument, 0, 'A'},
183 {"header", required_argument, 0, 'k'},
184 {"no-body", no_argument, 0, 'N'},
185 {"max-age", required_argument, 0, 'M'},
186 {"content-type", required_argument, 0, 'T'},
187 {"pagesize", required_argument, 0, 'm'},
188 {"invert-regex", no_argument, NULL, INVERT_REGEX},
189 {"use-ipv4", no_argument, 0, '4'},
190 {"use-ipv6", no_argument, 0, '6'},
191 {0, 0, 0, 0}
192 };
194 if (argc < 2)
195 return ERROR;
197 for (c = 1; c < argc; c++) {
198 if (strcmp ("-to", argv[c]) == 0)
199 strcpy (argv[c], "-t");
200 if (strcmp ("-hn", argv[c]) == 0)
201 strcpy (argv[c], "-H");
202 if (strcmp ("-wt", argv[c]) == 0)
203 strcpy (argv[c], "-w");
204 if (strcmp ("-ct", argv[c]) == 0)
205 strcpy (argv[c], "-c");
206 if (strcmp ("-nohtml", argv[c]) == 0)
207 strcpy (argv[c], "-n");
208 }
210 while (1) {
211 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);
212 if (c == -1 || c == EOF)
213 break;
215 switch (c) {
216 case '?': /* usage */
217 usage2 (_("Unknown argument"), optarg);
218 break;
219 case 'h': /* help */
220 print_help ();
221 exit (STATE_OK);
222 break;
223 case 'V': /* version */
224 print_revision (progname, revision);
225 exit (STATE_OK);
226 break;
227 case 't': /* timeout period */
228 if (!is_intnonneg (optarg))
229 usage2 (_("Timeout interval must be a positive integer"), optarg);
230 else
231 socket_timeout = atoi (optarg);
232 break;
233 case 'c': /* critical time threshold */
234 if (!is_nonnegative (optarg))
235 usage2 (_("Critical threshold must be integer"), optarg);
236 else {
237 critical_time = strtod (optarg, NULL);
238 check_critical_time = TRUE;
239 }
240 break;
241 case 'w': /* warning time threshold */
242 if (!is_nonnegative (optarg))
243 usage2 (_("Warning threshold must be integer"), optarg);
244 else {
245 warning_time = strtod (optarg, NULL);
246 check_warning_time = TRUE;
247 }
248 break;
249 case 'A': /* User Agent String */
250 asprintf (&user_agent, "User-Agent: %s", optarg);
251 break;
252 case 'k': /* Additional headers */
253 if (http_opt_headers_count == 0)
254 http_opt_headers = malloc (sizeof (char *) * (++http_opt_headers_count));
255 else
256 http_opt_headers = realloc (http_opt_headers, sizeof (char *) * (++http_opt_headers_count));
257 http_opt_headers[http_opt_headers_count - 1] = optarg;
258 //asprintf (&http_opt_headers, "%s", optarg);
259 break;
260 case 'L': /* show html link */
261 display_html = TRUE;
262 break;
263 case 'n': /* do not show html link */
264 display_html = FALSE;
265 break;
266 case 'C': /* Check SSL cert validity */
267 #ifdef HAVE_SSL
268 if (!is_intnonneg (optarg))
269 usage2 (_("Invalid certificate expiration period"), optarg);
270 else {
271 days_till_exp = atoi (optarg);
272 check_cert = TRUE;
273 }
274 /* Fall through to -S option */
275 #endif
276 case 'S': /* use SSL */
277 #ifndef HAVE_SSL
278 usage4 (_("Invalid option - SSL is not available"));
279 #endif
280 use_ssl = TRUE;
281 if (specify_port == FALSE)
282 server_port = HTTPS_PORT;
283 break;
284 case 'f': /* onredirect */
285 if (!strcmp (optarg, "follow"))
286 onredirect = STATE_DEPENDENT;
287 if (!strcmp (optarg, "unknown"))
288 onredirect = STATE_UNKNOWN;
289 if (!strcmp (optarg, "ok"))
290 onredirect = STATE_OK;
291 if (!strcmp (optarg, "warning"))
292 onredirect = STATE_WARNING;
293 if (!strcmp (optarg, "critical"))
294 onredirect = STATE_CRITICAL;
295 if (verbose)
296 printf(_("option f:%d \n"), onredirect);
297 break;
298 /* Note: H, I, and u must be malloc'd or will fail on redirects */
299 case 'H': /* Host Name (virtual host) */
300 host_name = strdup (optarg);
301 if (strstr (optarg, ":"))
302 sscanf (optarg, "%*[^:]:%d", &server_port);
303 break;
304 case 'I': /* Server IP-address */
305 server_address = strdup (optarg);
306 break;
307 case 'u': /* URL path */
308 server_url = strdup (optarg);
309 server_url_length = strlen (server_url);
310 break;
311 case 'p': /* Server port */
312 if (!is_intnonneg (optarg))
313 usage2 (_("Invalid port number"), optarg);
314 else {
315 server_port = atoi (optarg);
316 specify_port = TRUE;
317 }
318 break;
319 case 'a': /* authorization info */
320 strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
321 user_auth[MAX_INPUT_BUFFER - 1] = 0;
322 break;
323 case 'P': /* HTTP POST data in URL encoded format */
324 if (http_method || http_post_data) break;
325 http_method = strdup("POST");
326 http_post_data = strdup (optarg);
327 break;
328 case 's': /* string or substring */
329 strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
330 string_expect[MAX_INPUT_BUFFER - 1] = 0;
331 break;
332 case 'e': /* string or substring */
333 strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
334 server_expect[MAX_INPUT_BUFFER - 1] = 0;
335 server_expect_yn = 1;
336 break;
337 case 'T': /* Content-type */
338 asprintf (&http_content_type, "%s", optarg);
339 break;
340 case 'l': /* linespan */
341 cflags &= ~REG_NEWLINE;
342 break;
343 case 'R': /* regex */
344 cflags |= REG_ICASE;
345 case 'r': /* regex */
346 strncpy (regexp, optarg, MAX_RE_SIZE - 1);
347 regexp[MAX_RE_SIZE - 1] = 0;
348 errcode = regcomp (&preg, regexp, cflags);
349 if (errcode != 0) {
350 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
351 printf (_("Could Not Compile Regular Expression: %s"), errbuf);
352 return ERROR;
353 }
354 break;
355 case INVERT_REGEX:
356 invert_regex = 1;
357 break;
358 case '4':
359 address_family = AF_INET;
360 break;
361 case '6':
362 #ifdef USE_IPV6
363 address_family = AF_INET6;
364 #else
365 usage4 (_("IPv6 support not available"));
366 #endif
367 break;
368 case 'v': /* verbose */
369 verbose = TRUE;
370 break;
371 case 'm': /* min_page_length */
372 {
373 char *tmp;
374 if (strchr(optarg, ':') != (char *)NULL) {
375 /* range, so get two values, min:max */
376 tmp = strtok(optarg, ":");
377 if (tmp == NULL) {
378 printf("Bad format: try \"-m min:max\"\n");
379 exit (STATE_WARNING);
380 } else
381 min_page_len = atoi(tmp);
383 tmp = strtok(NULL, ":");
384 if (tmp == NULL) {
385 printf("Bad format: try \"-m min:max\"\n");
386 exit (STATE_WARNING);
387 } else
388 max_page_len = atoi(tmp);
389 } else
390 min_page_len = atoi (optarg);
391 break;
392 }
393 case 'N': /* no-body */
394 no_body = TRUE;
395 break;
396 case 'M': /* max-age */
397 {
398 int L = strlen(optarg);
399 if (L && optarg[L-1] == 'm')
400 maximum_age = atoi (optarg) * 60;
401 else if (L && optarg[L-1] == 'h')
402 maximum_age = atoi (optarg) * 60 * 60;
403 else if (L && optarg[L-1] == 'd')
404 maximum_age = atoi (optarg) * 60 * 60 * 24;
405 else if (L && (optarg[L-1] == 's' ||
406 isdigit (optarg[L-1])))
407 maximum_age = atoi (optarg);
408 else {
409 fprintf (stderr, "unparsable max-age: %s\n", optarg);
410 exit (STATE_WARNING);
411 }
412 }
413 break;
414 }
415 }
417 c = optind;
419 if (server_address == NULL && c < argc)
420 server_address = strdup (argv[c++]);
422 if (host_name == NULL && c < argc)
423 host_name = strdup (argv[c++]);
425 if (server_address == NULL) {
426 if (host_name == NULL)
427 usage4 (_("You must specify a server address or host name"));
428 else
429 server_address = strdup (host_name);
430 }
432 if (check_critical_time && critical_time>(double)socket_timeout)
433 socket_timeout = (int)critical_time + 1;
435 if (http_method == NULL)
436 http_method = strdup ("GET");
438 return TRUE;
439 }
443 /* written by lauri alanko */
444 static char *
445 base64 (const char *bin, size_t len)
446 {
448 char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
449 size_t i = 0, j = 0;
451 char BASE64_END = '=';
452 char base64_table[64];
453 strncpy (base64_table, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 64);
455 while (j < len - 2) {
456 buf[i++] = base64_table[bin[j] >> 2];
457 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
458 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
459 buf[i++] = base64_table[bin[j + 2] & 63];
460 j += 3;
461 }
463 switch (len - j) {
464 case 1:
465 buf[i++] = base64_table[bin[j] >> 2];
466 buf[i++] = base64_table[(bin[j] & 3) << 4];
467 buf[i++] = BASE64_END;
468 buf[i++] = BASE64_END;
469 break;
470 case 2:
471 buf[i++] = base64_table[bin[j] >> 2];
472 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
473 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
474 buf[i++] = BASE64_END;
475 break;
476 case 0:
477 break;
478 }
480 buf[i] = '\0';
481 return buf;
482 }
486 /* Returns 1 if we're done processing the document body; 0 to keep going */
487 static int
488 document_headers_done (char *full_page)
489 {
490 const char *body;
492 for (body = full_page; *body; body++) {
493 if (!strncmp (body, "\n\n", 2) || !strncmp (body, "\n\r\n", 3))
494 break;
495 }
497 if (!*body)
498 return 0; /* haven't read end of headers yet */
500 full_page[body - full_page] = 0;
501 return 1;
502 }
504 static time_t
505 parse_time_string (const char *string)
506 {
507 struct tm tm;
508 time_t t;
509 memset (&tm, 0, sizeof(tm));
511 /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */
513 if (isupper (string[0]) && /* Tue */
514 islower (string[1]) &&
515 islower (string[2]) &&
516 ',' == string[3] &&
517 ' ' == string[4] &&
518 (isdigit(string[5]) || string[5] == ' ') && /* 25 */
519 isdigit (string[6]) &&
520 ' ' == string[7] &&
521 isupper (string[8]) && /* Dec */
522 islower (string[9]) &&
523 islower (string[10]) &&
524 ' ' == string[11] &&
525 isdigit (string[12]) && /* 2001 */
526 isdigit (string[13]) &&
527 isdigit (string[14]) &&
528 isdigit (string[15]) &&
529 ' ' == string[16] &&
530 isdigit (string[17]) && /* 02: */
531 isdigit (string[18]) &&
532 ':' == string[19] &&
533 isdigit (string[20]) && /* 59: */
534 isdigit (string[21]) &&
535 ':' == string[22] &&
536 isdigit (string[23]) && /* 03 */
537 isdigit (string[24]) &&
538 ' ' == string[25] &&
539 'G' == string[26] && /* GMT */
540 'M' == string[27] && /* GMT */
541 'T' == string[28]) {
543 tm.tm_sec = 10 * (string[23]-'0') + (string[24]-'0');
544 tm.tm_min = 10 * (string[20]-'0') + (string[21]-'0');
545 tm.tm_hour = 10 * (string[17]-'0') + (string[18]-'0');
546 tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5]-'0') + (string[6]-'0');
547 tm.tm_mon = (!strncmp (string+8, "Jan", 3) ? 0 :
548 !strncmp (string+8, "Feb", 3) ? 1 :
549 !strncmp (string+8, "Mar", 3) ? 2 :
550 !strncmp (string+8, "Apr", 3) ? 3 :
551 !strncmp (string+8, "May", 3) ? 4 :
552 !strncmp (string+8, "Jun", 3) ? 5 :
553 !strncmp (string+8, "Jul", 3) ? 6 :
554 !strncmp (string+8, "Aug", 3) ? 7 :
555 !strncmp (string+8, "Sep", 3) ? 8 :
556 !strncmp (string+8, "Oct", 3) ? 9 :
557 !strncmp (string+8, "Nov", 3) ? 10 :
558 !strncmp (string+8, "Dec", 3) ? 11 :
559 -1);
560 tm.tm_year = ((1000 * (string[12]-'0') +
561 100 * (string[13]-'0') +
562 10 * (string[14]-'0') +
563 (string[15]-'0'))
564 - 1900);
566 tm.tm_isdst = 0; /* GMT is never in DST, right? */
568 if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31)
569 return 0;
571 /*
572 This is actually wrong: we need to subtract the local timezone
573 offset from GMT from this value. But, that's ok in this usage,
574 because we only comparing these two GMT dates against each other,
575 so it doesn't matter what time zone we parse them in.
576 */
578 t = mktime (&tm);
579 if (t == (time_t) -1) t = 0;
581 if (verbose) {
582 const char *s = string;
583 while (*s && *s != '\r' && *s != '\n')
584 fputc (*s++, stdout);
585 printf (" ==> %lu\n", (unsigned long) t);
586 }
588 return t;
590 } else {
591 return 0;
592 }
593 }
597 static void
598 check_document_dates (const char *headers)
599 {
600 const char *s;
601 char *server_date = 0;
602 char *document_date = 0;
604 s = headers;
605 while (*s) {
606 const char *field = s;
607 const char *value = 0;
609 /* Find the end of the header field */
610 while (*s && !isspace(*s) && *s != ':')
611 s++;
613 /* Remember the header value, if any. */
614 if (*s == ':')
615 value = ++s;
617 /* Skip to the end of the header, including continuation lines. */
618 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
619 s++;
620 s++;
622 /* Process this header. */
623 if (value && value > field+2) {
624 char *ff = (char *) malloc (value-field);
625 char *ss = ff;
626 while (field < value-1)
627 *ss++ = tolower(*field++);
628 *ss++ = 0;
630 if (!strcmp (ff, "date") || !strcmp (ff, "last-modified")) {
631 const char *e;
632 while (*value && isspace (*value))
633 value++;
634 for (e = value; *e && *e != '\r' && *e != '\n'; e++)
635 ;
636 ss = (char *) malloc (e - value + 1);
637 strncpy (ss, value, e - value);
638 ss[e - value] = 0;
639 if (!strcmp (ff, "date")) {
640 if (server_date) free (server_date);
641 server_date = ss;
642 } else {
643 if (document_date) free (document_date);
644 document_date = ss;
645 }
646 }
647 free (ff);
648 }
649 }
651 /* Done parsing the body. Now check the dates we (hopefully) parsed. */
652 if (!server_date || !*server_date) {
653 die (STATE_UNKNOWN, _("Server date unknown\n"));
654 } else if (!document_date || !*document_date) {
655 die (STATE_CRITICAL, _("Document modification date unknown\n"));
656 } else {
657 time_t srv_data = parse_time_string (server_date);
658 time_t doc_data = parse_time_string (document_date);
660 if (srv_data <= 0) {
661 die (STATE_CRITICAL, _("CRITICAL - Server date \"%100s\" unparsable"), server_date);
662 } else if (doc_data <= 0) {
663 die (STATE_CRITICAL, _("CRITICAL - Document date \"%100s\" unparsable"), document_date);
664 } else if (doc_data > srv_data + 30) {
665 die (STATE_CRITICAL, _("CRITICAL - Document is %d seconds in the future\n"), (int)doc_data - (int)srv_data);
666 } else if (doc_data < srv_data - maximum_age) {
667 int n = (srv_data - doc_data);
668 if (n > (60 * 60 * 24 * 2))
669 die (STATE_CRITICAL,
670 _("CRITICAL - Last modified %.1f days ago\n"),
671 ((float) n) / (60 * 60 * 24));
672 else
673 die (STATE_CRITICAL,
674 _("CRITICAL - Last modified %d:%02d:%02d ago\n"),
675 n / (60 * 60), (n / 60) % 60, n % 60);
676 }
678 free (server_date);
679 free (document_date);
680 }
681 }
683 int
684 get_content_length (const char *headers)
685 {
686 const char *s;
687 int content_length = 0;
689 s = headers;
690 while (*s) {
691 const char *field = s;
692 const char *value = 0;
694 /* Find the end of the header field */
695 while (*s && !isspace(*s) && *s != ':')
696 s++;
698 /* Remember the header value, if any. */
699 if (*s == ':')
700 value = ++s;
702 /* Skip to the end of the header, including continuation lines. */
703 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
704 s++;
705 s++;
707 /* Process this header. */
708 if (value && value > field+2) {
709 char *ff = (char *) malloc (value-field);
710 char *ss = ff;
711 while (field < value-1)
712 *ss++ = tolower(*field++);
713 *ss++ = 0;
715 if (!strcmp (ff, "content-length")) {
716 const char *e;
717 while (*value && isspace (*value))
718 value++;
719 for (e = value; *e && *e != '\r' && *e != '\n'; e++)
720 ;
721 ss = (char *) malloc (e - value + 1);
722 strncpy (ss, value, e - value);
723 ss[e - value] = 0;
724 content_length = atoi(ss);
725 free (ss);
726 }
727 free (ff);
728 }
729 }
730 return (content_length);
731 }
733 int
734 check_http (void)
735 {
736 char *msg;
737 char *status_line;
738 char *status_code;
739 char *header;
740 char *page;
741 char *auth;
742 int http_status;
743 int i = 0;
744 size_t pagesize = 0;
745 char *full_page;
746 char *buf;
747 char *pos;
748 long microsec;
749 double elapsed_time;
750 int page_len = 0;
751 int result = STATE_UNKNOWN;
753 /* try to connect to the host at the given port number */
754 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
755 die (STATE_CRITICAL, _("Unable to open TCP socket\n"));
756 #ifdef HAVE_SSL
757 if (use_ssl == TRUE) {
758 np_net_ssl_init(sd);
759 if (check_cert == TRUE) {
760 result = np_net_ssl_check_cert(days_till_exp);
761 np_net_ssl_cleanup();
762 if(sd) close(sd);
763 return result;
764 }
765 }
766 #endif /* HAVE_SSL */
768 asprintf (&buf, "%s %s HTTP/1.0\r\n%s\r\n", http_method, server_url, user_agent);
770 /* optionally send the host header info */
771 if (host_name)
772 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
774 /* optionally send any other header tag */
775 if (http_opt_headers_count) {
776 for (i = 0; i < http_opt_headers_count ; i++) {
777 for ((pos = strtok(http_opt_headers[i], INPUT_DELIMITER)); pos; (pos = strtok(NULL, INPUT_DELIMITER)))
778 asprintf (&buf, "%s%s\r\n", buf, pos);
779 }
780 free(http_opt_headers);
781 }
783 /* optionally send the authentication info */
784 if (strlen(user_auth)) {
785 auth = base64 (user_auth, strlen (user_auth));
786 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
787 }
789 /* either send http POST data */
790 if (http_post_data) {
791 if (http_content_type) {
792 asprintf (&buf, "%sContent-Type: %s\r\n", buf, http_content_type);
793 } else {
794 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
795 }
797 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, (int)strlen (http_post_data));
798 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
799 }
800 else {
801 /* or just a newline so the server knows we're done with the request */
802 asprintf (&buf, "%s%s", buf, CRLF);
803 }
805 if (verbose) printf ("%s\n", buf);
806 my_send (buf, strlen (buf));
808 /* fetch the page */
809 full_page = strdup("");
810 while ((i = my_recv (buffer, MAX_INPUT_BUFFER-1)) > 0) {
811 buffer[i] = '\0';
812 asprintf (&full_page, "%s%s", full_page, buffer);
813 pagesize += i;
815 if (no_body && document_headers_done (full_page)) {
816 i = 0;
817 break;
818 }
819 }
821 if (i < 0 && errno != ECONNRESET) {
822 #ifdef HAVE_SSL
823 /*
824 if (use_ssl) {
825 sslerr=SSL_get_error(ssl, i);
826 if ( sslerr == SSL_ERROR_SSL ) {
827 die (STATE_WARNING, _("Client Certificate Required\n"));
828 } else {
829 die (STATE_CRITICAL, _("Error on receive\n"));
830 }
831 }
832 else {
833 */
834 #endif
835 die (STATE_CRITICAL, _("Error on receive\n"));
836 #ifdef HAVE_SSL
837 /* XXX
838 }
839 */
840 #endif
841 }
843 /* return a CRITICAL status if we couldn't read any data */
844 if (pagesize == (size_t) 0)
845 die (STATE_CRITICAL, _("No data received %s\n"), timestamp);
847 /* close the connection */
848 #ifdef HAVE_SSL
849 np_net_ssl_cleanup();
850 #endif
851 if(sd) close(sd);
853 /* reset the alarm */
854 alarm (0);
856 /* leave full_page untouched so we can free it later */
857 page = full_page;
859 if (verbose)
860 printf ("%s://%s:%d%s is %d characters\n",
861 use_ssl ? "https" : "http", server_address,
862 server_port, server_url, (int)pagesize);
864 /* find status line and null-terminate it */
865 status_line = page;
866 page += (size_t) strcspn (page, "\r\n");
867 pos = page;
868 page += (size_t) strspn (page, "\r\n");
869 status_line[strcspn(status_line, "\r\n")] = 0;
870 strip (status_line);
871 if (verbose)
872 printf ("STATUS: %s\n", status_line);
874 /* find header info and null-terminate it */
875 header = page;
876 while (strcspn (page, "\r\n") > 0) {
877 page += (size_t) strcspn (page, "\r\n");
878 pos = page;
879 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
880 (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
881 page += (size_t) 2;
882 else
883 page += (size_t) 1;
884 }
885 page += (size_t) strspn (page, "\r\n");
886 header[pos - header] = 0;
887 if (verbose)
888 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header,
889 (no_body ? " [[ skipped ]]" : page));
891 /* make sure the status line matches the response we are looking for */
892 if (!strstr (status_line, server_expect)) {
893 if (server_port == HTTP_PORT)
894 asprintf (&msg,
895 _("Invalid HTTP response received from host\n"));
896 else
897 asprintf (&msg,
898 _("Invalid HTTP response received from host on port %d\n"),
899 server_port);
900 die (STATE_CRITICAL, "%s", msg);
901 }
903 /* Exit here if server_expect was set by user and not default */
904 if ( server_expect_yn ) {
905 asprintf (&msg,
906 _("HTTP OK: Status line output matched \"%s\"\n"),
907 server_expect);
908 if (verbose)
909 printf ("%s\n",msg);
910 }
911 else {
912 /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
913 /* HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
914 /* Status-Code = 3 DIGITS */
916 status_code = strchr (status_line, ' ') + sizeof (char);
917 if (strspn (status_code, "1234567890") != 3)
918 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line);
920 http_status = atoi (status_code);
922 /* check the return code */
924 if (http_status >= 600 || http_status < 100)
925 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line);
927 /* server errors result in a critical state */
928 else if (http_status >= 500)
929 die (STATE_CRITICAL, _("HTTP CRITICAL: %s\n"), status_line);
931 /* client errors result in a warning state */
932 else if (http_status >= 400)
933 die (STATE_WARNING, _("HTTP WARNING: %s\n"), status_line);
935 /* check redirected page if specified */
936 else if (http_status >= 300) {
938 if (onredirect == STATE_DEPENDENT)
939 redir (header, status_line);
940 else if (onredirect == STATE_UNKNOWN)
941 printf (_("UNKNOWN"));
942 else if (onredirect == STATE_OK)
943 printf (_("OK"));
944 else if (onredirect == STATE_WARNING)
945 printf (_("WARNING"));
946 else if (onredirect == STATE_CRITICAL)
947 printf (_("CRITICAL"));
948 microsec = deltime (tv);
949 elapsed_time = (double)microsec / 1.0e6;
950 die (onredirect,
951 _(" - %s - %.3f second response time %s%s|%s %s\n"),
952 status_line, elapsed_time, timestamp,
953 (display_html ? "</A>" : ""),
954 perfd_time (elapsed_time), perfd_size (pagesize));
955 } /* end if (http_status >= 300) */
957 } /* end else (server_expect_yn) */
959 if (maximum_age >= 0) {
960 check_document_dates (header);
961 }
963 /* check elapsed time */
964 microsec = deltime (tv);
965 elapsed_time = (double)microsec / 1.0e6;
966 asprintf (&msg,
967 _("HTTP WARNING: %s - %.3f second response time %s%s|%s %s\n"),
968 status_line, elapsed_time, timestamp,
969 (display_html ? "</A>" : ""),
970 perfd_time (elapsed_time), perfd_size (pagesize));
971 if (check_critical_time == TRUE && elapsed_time > critical_time)
972 die (STATE_CRITICAL, "%s", msg);
973 if (check_warning_time == TRUE && elapsed_time > warning_time)
974 die (STATE_WARNING, "%s", msg);
976 /* Page and Header content checks go here */
977 /* these checks should be last */
979 if (strlen (string_expect)) {
980 if (strstr (page, string_expect)) {
981 printf (_("HTTP OK %s - %.3f second response time %s%s|%s %s\n"),
982 status_line, elapsed_time,
983 timestamp, (display_html ? "</A>" : ""),
984 perfd_time (elapsed_time), perfd_size (pagesize));
985 exit (STATE_OK);
986 }
987 else {
988 printf (_("CRITICAL - string not found%s|%s %s\n"),
989 (display_html ? "</A>" : ""),
990 perfd_time (elapsed_time), perfd_size (pagesize));
991 exit (STATE_CRITICAL);
992 }
993 }
995 if (strlen (regexp)) {
996 errcode = regexec (&preg, page, REGS, pmatch, 0);
997 if ((errcode == 0 && invert_regex == 0) || (errcode == REG_NOMATCH && invert_regex == 1)) {
998 printf (_("HTTP OK %s - %.3f second response time %s%s|%s %s\n"),
999 status_line, elapsed_time,
1000 timestamp, (display_html ? "</A>" : ""),
1001 perfd_time (elapsed_time), perfd_size (pagesize));
1002 exit (STATE_OK);
1003 }
1004 else if ((errcode == REG_NOMATCH && invert_regex == 0) || (errcode == 0 && invert_regex == 1)) {
1005 if (invert_regex == 0)
1006 msg = strdup(_("pattern not found"));
1007 else
1008 msg = strdup(_("pattern found"));
1009 printf (_("%s - %s%s|%s %s\n"),
1010 _("CRITICAL"),
1011 msg,
1012 (display_html ? "</A>" : ""),
1013 perfd_time (elapsed_time), perfd_size (pagesize));
1014 exit (STATE_CRITICAL);
1015 }
1016 else {
1017 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1018 printf (_("CRITICAL - Execute Error: %s\n"), errbuf);
1019 exit (STATE_CRITICAL);
1020 }
1021 }
1023 /* make sure the page is of an appropriate size */
1024 /* page_len = get_content_length(header); */
1025 page_len = pagesize;
1026 if ((max_page_len > 0) && (page_len > max_page_len)) {
1027 printf (_("HTTP WARNING: page size %d too large%s|%s\n"),
1028 page_len, (display_html ? "</A>" : ""), perfd_size (page_len) );
1029 exit (STATE_WARNING);
1030 } else if ((min_page_len > 0) && (page_len < min_page_len)) {
1031 printf (_("HTTP WARNING: page size %d too small%s|%s\n"),
1032 page_len, (display_html ? "</A>" : ""), perfd_size (page_len) );
1033 exit (STATE_WARNING);
1034 }
1035 /* We only get here if all tests have been passed */
1036 asprintf (&msg, _("HTTP OK %s - %d bytes in %.3f seconds %s%s|%s %s\n"),
1037 status_line, page_len, elapsed_time,
1038 timestamp, (display_html ? "</A>" : ""),
1039 perfd_time (elapsed_time), perfd_size (page_len));
1040 die (STATE_OK, "%s", msg);
1041 return STATE_UNKNOWN;
1042 }
1046 /* per RFC 2396 */
1047 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
1048 #define URI_HTTP "%[HTPShtps]://"
1049 #define URI_HOST "%[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1050 #define URI_PORT ":%[0123456789]"
1051 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1052 #define HD1 URI_HTTP URI_HOST URI_PORT URI_PATH
1053 #define HD2 URI_HTTP URI_HOST URI_PATH
1054 #define HD3 URI_HTTP URI_HOST URI_PORT
1055 #define HD4 URI_HTTP URI_HOST
1056 #define HD5 URI_PATH
1058 void
1059 redir (char *pos, char *status_line)
1060 {
1061 int i = 0;
1062 char *x;
1063 char xx[2];
1064 char type[6];
1065 char *addr;
1066 char port[6];
1067 char *url;
1069 addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
1070 if (addr == NULL)
1071 die (STATE_UNKNOWN, _("Could not allocate addr\n"));
1073 url = malloc (strcspn (pos, "\r\n"));
1074 if (url == NULL)
1075 die (STATE_UNKNOWN, _("Could not allocate url\n"));
1077 while (pos) {
1079 if (sscanf (pos, "%[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]:%n", xx, &i) < 1) {
1081 pos += (size_t) strcspn (pos, "\r\n");
1082 pos += (size_t) strspn (pos, "\r\n");
1083 if (strlen(pos) == 0)
1084 die (STATE_UNKNOWN,
1085 _("UNKNOWN - Could not find redirect location - %s%s\n"),
1086 status_line, (display_html ? "</A>" : ""));
1087 continue;
1088 }
1090 pos += i;
1091 pos += strspn (pos, " \t\r\n");
1093 url = realloc (url, strcspn (pos, "\r\n"));
1094 if (url == NULL)
1095 die (STATE_UNKNOWN, _("could not allocate url\n"));
1097 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
1098 if (sscanf (pos, HD1, type, addr, port, url) == 4) {
1099 use_ssl = server_type_check (type);
1100 i = atoi (port);
1101 }
1103 /* URI_HTTP URI_HOST URI_PATH */
1104 else if (sscanf (pos, HD2, type, addr, url) == 3 ) {
1105 use_ssl = server_type_check (type);
1106 i = server_port_check (use_ssl);
1107 }
1109 /* URI_HTTP URI_HOST URI_PORT */
1110 else if(sscanf (pos, HD3, type, addr, port) == 3) {
1111 strcpy (url, HTTP_URL);
1112 use_ssl = server_type_check (type);
1113 i = atoi (port);
1114 }
1116 /* URI_HTTP URI_HOST */
1117 else if(sscanf (pos, HD4, type, addr) == 2) {
1118 strcpy (url, HTTP_URL);
1119 use_ssl = server_type_check (type);
1120 i = server_port_check (use_ssl);
1121 }
1123 /* URI_PATH */
1124 else if (sscanf (pos, HD5, url) == 1) {
1125 /* relative url */
1126 if ((url[0] != '/')) {
1127 if ((x = strrchr(server_url, '/')))
1128 *x = '\0';
1129 asprintf (&url, "%s/%s", server_url, url);
1130 }
1131 i = server_port;
1132 strcpy (type, server_type);
1133 strcpy (addr, host_name);
1134 }
1136 else {
1137 die (STATE_UNKNOWN,
1138 _("UNKNOWN - Could not parse redirect location - %s%s\n"),
1139 pos, (display_html ? "</A>" : ""));
1140 }
1142 break;
1144 } /* end while (pos) */
1146 if (++redir_depth > max_depth)
1147 die (STATE_WARNING,
1148 _("WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
1149 max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
1151 if (server_port==i &&
1152 !strcmp(server_address, addr) &&
1153 (host_name && !strcmp(host_name, addr)) &&
1154 !strcmp(server_url, url))
1155 die (STATE_WARNING,
1156 _("WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1157 type, addr, i, url, (display_html ? "</A>" : ""));
1159 server_port = i;
1160 strcpy (server_type, type);
1162 free (host_name);
1163 host_name = strdup (addr);
1165 free (server_address);
1166 server_address = strdup (addr);
1168 free (server_url);
1169 server_url = strdup (url);
1171 check_http ();
1172 }
1176 int
1177 server_type_check (const char *type)
1178 {
1179 if (strcmp (type, "https"))
1180 return FALSE;
1181 else
1182 return TRUE;
1183 }
1185 int
1186 server_port_check (int ssl_flag)
1187 {
1188 if (ssl_flag)
1189 return HTTPS_PORT;
1190 else
1191 return HTTP_PORT;
1192 }
1194 char *perfd_time (double elapsed_time)
1195 {
1196 return fperfdata ("time", elapsed_time, "s",
1197 check_warning_time, warning_time,
1198 check_critical_time, critical_time,
1199 TRUE, 0, FALSE, 0);
1200 }
1204 char *perfd_size (int page_len)
1205 {
1206 return perfdata ("size", page_len, "B",
1207 (min_page_len>0?TRUE:FALSE), min_page_len,
1208 (min_page_len>0?TRUE:FALSE), 0,
1209 TRUE, 0, FALSE, 0);
1210 }
1212 void
1213 print_help (void)
1214 {
1215 print_revision (progname, revision);
1217 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1218 printf (COPYRIGHT, copyright, email);
1220 printf (_("\
1221 This plugin tests the HTTP service on the specified host. It can test\n\
1222 normal (http) and secure (https) servers, follow redirects, search for\n\
1223 strings and regular expressions, check connection times, and report on\n\
1224 certificate expiration times."));
1226 printf ("\n\n");
1228 print_usage ();
1230 printf (_("NOTE: One or both of -H and -I must be specified"));
1232 printf ("\n");
1234 printf (_(UT_HELP_VRSN));
1236 printf (_("\
1237 -H, --hostname=ADDRESS\n\
1238 Host name argument for servers using host headers (virtual host)\n\
1239 Append a port to include it in the header (eg: example.com:5000)\n\
1240 -I, --IP-address=ADDRESS\n\
1241 IP address or name (use numeric address if possible to bypass DNS lookup).\n\
1242 -p, --port=INTEGER\n\
1243 Port number (default: %d)\n"), HTTP_PORT);
1245 printf (_(UT_IPv46));
1247 #ifdef HAVE_SSL
1248 printf (_("\
1249 -S, --ssl\n\
1250 Connect via SSL\n\
1251 -C, --certificate=INTEGER\n\
1252 Minimum number of days a certificate has to be valid.\n\
1253 (when this option is used the url is not checked.)\n"));
1254 #endif
1256 printf (_("\
1257 -e, --expect=STRING\n\
1258 String to expect in first (status) line of server response (default: %s)\n\
1259 If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)\n\
1260 -s, --string=STRING\n\
1261 String to expect in the content\n\
1262 -u, --url=PATH\n\
1263 URL to GET or POST (default: /)\n\
1264 -P, --post=STRING\n\
1265 URL encoded http POST data\n\
1266 -N, --no-body\n\
1267 Don't wait for document body: stop reading after headers.\n\
1268 (Note that this still does an HTTP GET or POST, not a HEAD.)\n\
1269 -M, --max-age=SECONDS\n\
1270 Warn if document is more than SECONDS old. the number can also be of \n\
1271 the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days.\n\
1272 -T, --content-type=STRING\n\
1273 specify Content-Type header media type when POSTing\n"), HTTP_EXPECT);
1275 printf (_("\
1276 -l, --linespan\n\
1277 Allow regex to span newlines (must precede -r or -R)\n\
1278 -r, --regex, --ereg=STRING\n\
1279 Search page for regex STRING\n\
1280 -R, --eregi=STRING\n\
1281 Search page for case-insensitive regex STRING\n\
1282 --invert-regex\n\
1283 Return CRITICAL if found, OK if not\n"));
1285 printf (_("\
1286 -a, --authorization=AUTH_PAIR\n\
1287 Username:password on sites with basic authentication\n\
1288 -A, --useragent=STRING\n\
1289 String to be sent in http header as \"User Agent\"\n\
1290 -k, --header=STRING\n\
1291 Any other tags to be sent in http header. Use multiple times for additional headers\n\
1292 -L, --link=URL\n\
1293 Wrap output in HTML link (obsoleted by urlize)\n\
1294 -f, --onredirect=<ok|warning|critical|follow>\n\
1295 How to handle redirected pages\n\
1296 -m, --pagesize=INTEGER<:INTEGER>\n\
1297 Minimum page size required (bytes) : Maximum page size required (bytes)\n"));
1299 printf (_(UT_WARN_CRIT));
1301 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
1303 printf (_(UT_VERBOSE));
1305 printf (_("\
1306 This plugin will attempt to open an HTTP connection with the host. Successful\n\
1307 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
1308 errors return STATE_UNKNOWN. Successful connects, but incorrect reponse\n\
1309 messages from the host result in STATE_WARNING return values. If you are\n\
1310 checking a virtual server that uses 'host headers' you must supply the FQDN\n\
1311 (fully qualified domain name) as the [host_name] argument.\n"));
1313 #ifdef HAVE_SSL
1314 printf (_("\n\
1315 This plugin can also check whether an SSL enabled web server is able to\n\
1316 serve content (optionally within a specified time) or whether the X509 \n\
1317 certificate is still valid for the specified number of days.\n"));
1318 printf (_("\n\
1319 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
1320 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
1321 STATE_OK will be returned. When the server returns its content but exceeds\n\
1322 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
1323 a STATE_CRITICAL will be returned.\n\n"));
1325 printf (_("\
1326 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
1327 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
1328 STATE_OK is returned. When the certificate is still valid, but for less than\n\
1329 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
1330 the certificate is expired.\n"));
1331 #endif
1333 printf (_(UT_SUPPORT));
1335 }
1339 void
1340 print_usage (void)
1341 {
1342 printf (_("Usage:"));
1343 printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname);
1344 printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n");
1345 printf (" [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n");
1346 printf (" [-s string] [-l] [-r <regex> | -R <case-insensitive regex>] [-P string]\n");
1347 printf (" [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>] [-A string] [-k string]\n");
1348 }