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