1 /*****************************************************************************
3 * Monitoring check_http plugin
6 * Copyright (c) 1999-2013 Monitoring 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-2013";
38 const char *email
= "devel@monitoring-plugins.org";
50 #define HTTP_EXPECT "HTTP/1."
52 MAX_IPV4_HOSTLENGTH
= 255,
59 int check_cert
= FALSE
;
61 int days_till_exp_warn
, days_till_exp_crit
;
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
;
87 struct timeval tv_temp
;
92 int specify_port
= FALSE
;
93 int server_port
= HTTP_PORT
;
95 char server_port_text
[6] = "";
96 char server_type
[6] = "http";
102 int server_url_length
;
103 int server_expect_yn
= 0;
104 char server_expect
[MAX_INPUT_BUFFER
] = HTTP_EXPECT
;
105 char header_expect
[MAX_INPUT_BUFFER
] = "";
106 char string_expect
[MAX_INPUT_BUFFER
] = "";
107 char output_header_search
[30] = "";
108 char output_string_search
[30] = "";
109 char *warning_thresholds
= NULL
;
110 char *critical_thresholds
= NULL
;
112 char user_auth
[MAX_INPUT_BUFFER
] = "";
113 char proxy_auth
[MAX_INPUT_BUFFER
] = "";
114 int display_html
= FALSE
;
115 char **http_opt_headers
;
116 int http_opt_headers_count
= 0;
117 int onredirect
= STATE_OK
;
118 int followsticky
= STICKY_NONE
;
122 int show_extended_perfdata
= FALSE
;
123 int show_body
= FALSE
;
125 int min_page_len
= 0;
126 int max_page_len
= 0;
130 char *http_method_proxy
;
131 char *http_post_data
;
132 char *http_content_type
;
133 char buffer
[MAX_INPUT_BUFFER
];
134 char *client_cert
= NULL
;
135 char *client_privkey
= NULL
;
137 int process_arguments (int, char **);
138 int check_http (void);
139 void redir (char *pos
, char *status_line
);
140 int server_type_check(const char *type
);
141 int server_port_check(int ssl_flag
);
142 char *perfd_time (double microsec
);
143 char *perfd_time_connect (double microsec
);
144 char *perfd_time_ssl (double microsec
);
145 char *perfd_time_firstbyte (double microsec
);
146 char *perfd_time_headers (double microsec
);
147 char *perfd_time_transfer (double microsec
);
148 char *perfd_size (int page_len
);
149 void print_help (void);
150 void print_usage (void);
153 main (int argc
, char **argv
)
155 int result
= STATE_UNKNOWN
;
157 setlocale (LC_ALL
, "");
158 bindtextdomain (PACKAGE
, LOCALEDIR
);
159 textdomain (PACKAGE
);
161 /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
162 server_url
= strdup(HTTP_URL
);
163 server_url_length
= strlen(server_url
);
164 xasprintf (&user_agent
, "User-Agent: check_http/v%s (monitoring-plugins %s)",
165 NP_VERSION
, VERSION
);
167 /* Parse extra opts if any */
168 argv
=np_extra_opts (&argc
, argv
, progname
);
170 if (process_arguments (argc
, argv
) == ERROR
)
171 usage4 (_("Could not parse arguments"));
173 if (display_html
== TRUE
)
174 printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">",
175 use_ssl
? "https" : "http", host_name
? host_name
: server_address
,
176 server_port
, server_url
);
178 /* initialize alarm signal handling, set socket timeout, start timer */
179 (void) signal (SIGALRM
, socket_timeout_alarm_handler
);
180 (void) alarm (socket_timeout
);
181 gettimeofday (&tv
, NULL
);
183 result
= check_http ();
187 /* check whether a file exists */
189 test_file (char *path
)
191 if (access(path
, R_OK
) == 0)
193 usage2 (_("file does not exist or is not readable"), path
);
196 /* process command-line arguments */
198 process_arguments (int argc
, char **argv
)
205 INVERT_REGEX
= CHAR_MAX
+ 1,
210 static struct option longopts
[] = {
212 {"link", no_argument
, 0, 'L'},
213 {"nohtml", no_argument
, 0, 'n'},
214 {"ssl", optional_argument
, 0, 'S'},
215 {"sni", no_argument
, 0, SNI_OPTION
},
216 {"post", required_argument
, 0, 'P'},
217 {"method", required_argument
, 0, 'j'},
218 {"IP-address", required_argument
, 0, 'I'},
219 {"url", required_argument
, 0, 'u'},
220 {"port", required_argument
, 0, 'p'},
221 {"authorization", required_argument
, 0, 'a'},
222 {"proxy-authorization", required_argument
, 0, 'b'},
223 {"header-string", required_argument
, 0, 'd'},
224 {"string", required_argument
, 0, 's'},
225 {"expect", required_argument
, 0, 'e'},
226 {"regex", required_argument
, 0, 'r'},
227 {"ereg", required_argument
, 0, 'r'},
228 {"eregi", required_argument
, 0, 'R'},
229 {"linespan", no_argument
, 0, 'l'},
230 {"onredirect", required_argument
, 0, 'f'},
231 {"certificate", required_argument
, 0, 'C'},
232 {"client-cert", required_argument
, 0, 'J'},
233 {"private-key", required_argument
, 0, 'K'},
234 {"useragent", required_argument
, 0, 'A'},
235 {"header", required_argument
, 0, 'k'},
236 {"no-body", no_argument
, 0, 'N'},
237 {"max-age", required_argument
, 0, 'M'},
238 {"content-type", required_argument
, 0, 'T'},
239 {"pagesize", required_argument
, 0, 'm'},
240 {"invert-regex", no_argument
, NULL
, INVERT_REGEX
},
241 {"use-ipv4", no_argument
, 0, '4'},
242 {"use-ipv6", no_argument
, 0, '6'},
243 {"extended-perfdata", no_argument
, 0, 'E'},
244 {"show-body", no_argument
, 0, 'B'},
251 for (c
= 1; c
< argc
; c
++) {
252 if (strcmp ("-to", argv
[c
]) == 0)
253 strcpy (argv
[c
], "-t");
254 if (strcmp ("-hn", argv
[c
]) == 0)
255 strcpy (argv
[c
], "-H");
256 if (strcmp ("-wt", argv
[c
]) == 0)
257 strcpy (argv
[c
], "-w");
258 if (strcmp ("-ct", argv
[c
]) == 0)
259 strcpy (argv
[c
], "-c");
260 if (strcmp ("-nohtml", argv
[c
]) == 0)
261 strcpy (argv
[c
], "-n");
265 c
= getopt_long (argc
, argv
, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:nlLS::m:M:NEB", longopts
, &option
);
266 if (c
== -1 || c
== EOF
)
270 case '?': /* usage */
275 exit (STATE_UNKNOWN
);
277 case 'V': /* version */
278 print_revision (progname
, NP_VERSION
);
279 exit (STATE_UNKNOWN
);
281 case 't': /* timeout period */
282 if (!is_intnonneg (optarg
))
283 usage2 (_("Timeout interval must be a positive integer"), optarg
);
285 socket_timeout
= atoi (optarg
);
287 case 'c': /* critical time threshold */
288 critical_thresholds
= optarg
;
290 case 'w': /* warning time threshold */
291 warning_thresholds
= optarg
;
293 case 'A': /* User Agent String */
294 xasprintf (&user_agent
, "User-Agent: %s", optarg
);
296 case 'k': /* Additional headers */
297 if (http_opt_headers_count
== 0)
298 http_opt_headers
= malloc (sizeof (char *) * (++http_opt_headers_count
));
300 http_opt_headers
= realloc (http_opt_headers
, sizeof (char *) * (++http_opt_headers_count
));
301 http_opt_headers
[http_opt_headers_count
- 1] = optarg
;
302 /* xasprintf (&http_opt_headers, "%s", optarg); */
304 case 'L': /* show html link */
307 case 'n': /* do not show html link */
308 display_html
= FALSE
;
310 case 'C': /* Check SSL cert validity */
312 if ((temp
=strchr(optarg
,','))!=NULL
) {
314 if (!is_intnonneg (optarg
))
315 usage2 (_("Invalid certificate expiration period"), optarg
);
316 days_till_exp_warn
= atoi(optarg
);
319 if (!is_intnonneg (temp
))
320 usage2 (_("Invalid certificate expiration period"), temp
);
321 days_till_exp_crit
= atoi (temp
);
324 days_till_exp_crit
=0;
325 if (!is_intnonneg (optarg
))
326 usage2 (_("Invalid certificate expiration period"), optarg
);
327 days_till_exp_warn
= atoi (optarg
);
332 case 'J': /* use client certificate */
335 client_cert
= optarg
;
338 case 'K': /* use client private key */
341 client_privkey
= optarg
;
344 case 'S': /* use SSL */
347 /* ssl_version initialized to 0 as a default. Only set if it's non-zero. This helps when we include multiple
348 parameters, like -S and -C combinations */
350 if (c
=='S' && optarg
!= NULL
) {
351 int got_plus
= strchr(optarg
, '+') != NULL
;
353 if (!strncmp (optarg
, "1.2", 3))
354 ssl_version
= got_plus
? MP_TLSv1_2_OR_NEWER
: MP_TLSv1_2
;
355 else if (!strncmp (optarg
, "1.1", 3))
356 ssl_version
= got_plus
? MP_TLSv1_1_OR_NEWER
: MP_TLSv1_1
;
357 else if (optarg
[0] == '1')
358 ssl_version
= got_plus
? MP_TLSv1_OR_NEWER
: MP_TLSv1
;
359 else if (optarg
[0] == '3')
360 ssl_version
= got_plus
? MP_SSLv3_OR_NEWER
: MP_SSLv3
;
361 else if (optarg
[0] == '2')
362 ssl_version
= got_plus
? MP_SSLv2_OR_NEWER
: MP_SSLv2
;
364 usage4 (_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2 (with optional '+' suffix)"));
366 if (specify_port
== FALSE
)
367 server_port
= HTTPS_PORT
;
369 /* -C -J and -K fall through to here without SSL */
370 usage4 (_("Invalid option - SSL is not available"));
376 case 'f': /* onredirect */
377 if (!strcmp (optarg
, "stickyport"))
378 onredirect
= STATE_DEPENDENT
, followsticky
= STICKY_HOST
|STICKY_PORT
;
379 else if (!strcmp (optarg
, "sticky"))
380 onredirect
= STATE_DEPENDENT
, followsticky
= STICKY_HOST
;
381 else if (!strcmp (optarg
, "follow"))
382 onredirect
= STATE_DEPENDENT
, followsticky
= STICKY_NONE
;
383 else if (!strcmp (optarg
, "unknown"))
384 onredirect
= STATE_UNKNOWN
;
385 else if (!strcmp (optarg
, "ok"))
386 onredirect
= STATE_OK
;
387 else if (!strcmp (optarg
, "warning"))
388 onredirect
= STATE_WARNING
;
389 else if (!strcmp (optarg
, "critical"))
390 onredirect
= STATE_CRITICAL
;
391 else usage2 (_("Invalid onredirect option"), optarg
);
393 printf(_("option f:%d \n"), onredirect
);
395 /* Note: H, I, and u must be malloc'd or will fail on redirects */
396 case 'H': /* Host Name (virtual host) */
397 host_name
= strdup (optarg
);
398 if (host_name
[0] == '[') {
399 if ((p
= strstr (host_name
, "]:")) != NULL
) { /* [IPv6]:port */
400 virtual_port
= atoi (p
+ 2);
401 /* cut off the port */
402 host_name_length
= strlen (host_name
) - strlen (p
) - 1;
404 host_name
= strndup (optarg
, host_name_length
);
405 if (specify_port
== FALSE
)
406 server_port
= virtual_port
;
408 } else if ((p
= strchr (host_name
, ':')) != NULL
409 && strchr (++p
, ':') == NULL
) { /* IPv4:port or host:port */
410 virtual_port
= atoi (p
);
411 /* cut off the port */
412 host_name_length
= strlen (host_name
) - strlen (p
) - 1;
414 host_name
= strndup (optarg
, host_name_length
);
415 if (specify_port
== FALSE
)
416 server_port
= virtual_port
;
419 case 'I': /* Server IP-address */
420 server_address
= strdup (optarg
);
422 case 'u': /* URL path */
423 server_url
= strdup (optarg
);
424 server_url_length
= strlen (server_url
);
426 case 'p': /* Server port */
427 if (!is_intnonneg (optarg
))
428 usage2 (_("Invalid port number"), optarg
);
430 server_port
= atoi (optarg
);
434 case 'a': /* authorization info */
435 strncpy (user_auth
, optarg
, MAX_INPUT_BUFFER
- 1);
436 user_auth
[MAX_INPUT_BUFFER
- 1] = 0;
438 case 'b': /* proxy-authorization info */
439 strncpy (proxy_auth
, optarg
, MAX_INPUT_BUFFER
- 1);
440 proxy_auth
[MAX_INPUT_BUFFER
- 1] = 0;
442 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
443 if (! http_post_data
)
444 http_post_data
= strdup (optarg
);
446 http_method
= strdup("POST");
448 case 'j': /* Set HTTP method */
451 http_method
= strdup (optarg
);
453 if ((tmp
= strstr(http_method
, ":")) > 0) {
455 http_method
= http_method
;
456 http_method_proxy
= ++tmp
;
459 case 'd': /* string or substring */
460 strncpy (header_expect
, optarg
, MAX_INPUT_BUFFER
- 1);
461 header_expect
[MAX_INPUT_BUFFER
- 1] = 0;
463 case 's': /* string or substring */
464 strncpy (string_expect
, optarg
, MAX_INPUT_BUFFER
- 1);
465 string_expect
[MAX_INPUT_BUFFER
- 1] = 0;
467 case 'e': /* string or substring */
468 strncpy (server_expect
, optarg
, MAX_INPUT_BUFFER
- 1);
469 server_expect
[MAX_INPUT_BUFFER
- 1] = 0;
470 server_expect_yn
= 1;
472 case 'T': /* Content-type */
473 xasprintf (&http_content_type
, "%s", optarg
);
475 case 'l': /* linespan */
476 cflags
&= ~REG_NEWLINE
;
478 case 'R': /* regex */
480 case 'r': /* regex */
481 strncpy (regexp
, optarg
, MAX_RE_SIZE
- 1);
482 regexp
[MAX_RE_SIZE
- 1] = 0;
483 errcode
= regcomp (&preg
, regexp
, cflags
);
485 (void) regerror (errcode
, &preg
, errbuf
, MAX_INPUT_BUFFER
);
486 printf (_("Could Not Compile Regular Expression: %s"), errbuf
);
494 address_family
= AF_INET
;
498 address_family
= AF_INET6
;
500 usage4 (_("IPv6 support not available"));
503 case 'v': /* verbose */
506 case 'm': /* min_page_length */
509 if (strchr(optarg
, ':') != (char *)NULL
) {
510 /* range, so get two values, min:max */
511 tmp
= strtok(optarg
, ":");
513 printf("Bad format: try \"-m min:max\"\n");
514 exit (STATE_WARNING
);
516 min_page_len
= atoi(tmp
);
518 tmp
= strtok(NULL
, ":");
520 printf("Bad format: try \"-m min:max\"\n");
521 exit (STATE_WARNING
);
523 max_page_len
= atoi(tmp
);
525 min_page_len
= atoi (optarg
);
528 case 'N': /* no-body */
531 case 'M': /* max-age */
533 int L
= strlen(optarg
);
534 if (L
&& optarg
[L
-1] == 'm')
535 maximum_age
= atoi (optarg
) * 60;
536 else if (L
&& optarg
[L
-1] == 'h')
537 maximum_age
= atoi (optarg
) * 60 * 60;
538 else if (L
&& optarg
[L
-1] == 'd')
539 maximum_age
= atoi (optarg
) * 60 * 60 * 24;
540 else if (L
&& (optarg
[L
-1] == 's' ||
541 isdigit (optarg
[L
-1])))
542 maximum_age
= atoi (optarg
);
544 fprintf (stderr
, "unparsable max-age: %s\n", optarg
);
545 exit (STATE_WARNING
);
549 case 'E': /* show extended perfdata */
550 show_extended_perfdata
= TRUE
;
552 case 'B': /* print body content after status line */
560 if (server_address
== NULL
&& c
< argc
)
561 server_address
= strdup (argv
[c
++]);
563 if (host_name
== NULL
&& c
< argc
)
564 host_name
= strdup (argv
[c
++]);
566 if (server_address
== NULL
) {
567 if (host_name
== NULL
)
568 usage4 (_("You must specify a server address or host name"));
570 server_address
= strdup (host_name
);
573 set_thresholds(&thlds
, warning_thresholds
, critical_thresholds
);
575 if (critical_thresholds
&& thlds
->critical
->end
>(double)socket_timeout
)
576 socket_timeout
= (int)thlds
->critical
->end
+ 1;
578 if (http_method
== NULL
)
579 http_method
= strdup ("GET");
581 if (http_method_proxy
== NULL
)
582 http_method_proxy
= strdup ("GET");
584 if (client_cert
&& !client_privkey
)
585 usage4 (_("If you use a client certificate you must also specify a private key file"));
587 if (virtual_port
== 0)
588 virtual_port
= server_port
;
595 /* Returns 1 if we're done processing the document body; 0 to keep going */
597 document_headers_done (char *full_page
)
601 for (body
= full_page
; *body
; body
++) {
602 if (!strncmp (body
, "\n\n", 2) || !strncmp (body
, "\n\r\n", 3))
607 return 0; /* haven't read end of headers yet */
609 full_page
[body
- full_page
] = 0;
614 parse_time_string (const char *string
)
618 memset (&tm
, 0, sizeof(tm
));
620 /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */
622 if (isupper (string
[0]) && /* Tue */
623 islower (string
[1]) &&
624 islower (string
[2]) &&
627 (isdigit(string
[5]) || string
[5] == ' ') && /* 25 */
628 isdigit (string
[6]) &&
630 isupper (string
[8]) && /* Dec */
631 islower (string
[9]) &&
632 islower (string
[10]) &&
634 isdigit (string
[12]) && /* 2001 */
635 isdigit (string
[13]) &&
636 isdigit (string
[14]) &&
637 isdigit (string
[15]) &&
639 isdigit (string
[17]) && /* 02: */
640 isdigit (string
[18]) &&
642 isdigit (string
[20]) && /* 59: */
643 isdigit (string
[21]) &&
645 isdigit (string
[23]) && /* 03 */
646 isdigit (string
[24]) &&
648 'G' == string
[26] && /* GMT */
649 'M' == string
[27] && /* GMT */
652 tm
.tm_sec
= 10 * (string
[23]-'0') + (string
[24]-'0');
653 tm
.tm_min
= 10 * (string
[20]-'0') + (string
[21]-'0');
654 tm
.tm_hour
= 10 * (string
[17]-'0') + (string
[18]-'0');
655 tm
.tm_mday
= 10 * (string
[5] == ' ' ? 0 : string
[5]-'0') + (string
[6]-'0');
656 tm
.tm_mon
= (!strncmp (string
+8, "Jan", 3) ? 0 :
657 !strncmp (string
+8, "Feb", 3) ? 1 :
658 !strncmp (string
+8, "Mar", 3) ? 2 :
659 !strncmp (string
+8, "Apr", 3) ? 3 :
660 !strncmp (string
+8, "May", 3) ? 4 :
661 !strncmp (string
+8, "Jun", 3) ? 5 :
662 !strncmp (string
+8, "Jul", 3) ? 6 :
663 !strncmp (string
+8, "Aug", 3) ? 7 :
664 !strncmp (string
+8, "Sep", 3) ? 8 :
665 !strncmp (string
+8, "Oct", 3) ? 9 :
666 !strncmp (string
+8, "Nov", 3) ? 10 :
667 !strncmp (string
+8, "Dec", 3) ? 11 :
669 tm
.tm_year
= ((1000 * (string
[12]-'0') +
670 100 * (string
[13]-'0') +
671 10 * (string
[14]-'0') +
675 tm
.tm_isdst
= 0; /* GMT is never in DST, right? */
677 if (tm
.tm_mon
< 0 || tm
.tm_mday
< 1 || tm
.tm_mday
> 31)
681 This is actually wrong: we need to subtract the local timezone
682 offset from GMT from this value. But, that's ok in this usage,
683 because we only comparing these two GMT dates against each other,
684 so it doesn't matter what time zone we parse them in.
688 if (t
== (time_t) -1) t
= 0;
691 const char *s
= string
;
692 while (*s
&& *s
!= '\r' && *s
!= '\n')
693 fputc (*s
++, stdout
);
694 printf (" ==> %lu\n", (unsigned long) t
);
704 /* Checks if the server 'reply' is one of the expected 'statuscodes' */
706 expected_statuscode (const char *reply
, const char *statuscodes
)
708 char *expected
, *code
;
711 if ((expected
= strdup (statuscodes
)) == NULL
)
712 die (STATE_UNKNOWN
, _("HTTP UNKNOWN - Memory allocation error\n"));
714 for (code
= strtok (expected
, ","); code
!= NULL
; code
= strtok (NULL
, ","))
715 if (strstr (reply
, code
) != NULL
) {
725 check_document_dates (const char *headers
, char **msg
)
728 char *server_date
= 0;
729 char *document_date
= 0;
730 int date_result
= STATE_OK
;
734 const char *field
= s
;
735 const char *value
= 0;
737 /* Find the end of the header field */
738 while (*s
&& !isspace(*s
) && *s
!= ':')
741 /* Remember the header value, if any. */
745 /* Skip to the end of the header, including continuation lines. */
746 while (*s
&& !(*s
== '\n' && (s
[1] != ' ' && s
[1] != '\t')))
749 /* Avoid stepping over end-of-string marker */
753 /* Process this header. */
754 if (value
&& value
> field
+2) {
755 char *ff
= (char *) malloc (value
-field
);
757 while (field
< value
-1)
758 *ss
++ = tolower(*field
++);
761 if (!strcmp (ff
, "date") || !strcmp (ff
, "last-modified")) {
763 while (*value
&& isspace (*value
))
765 for (e
= value
; *e
&& *e
!= '\r' && *e
!= '\n'; e
++)
767 ss
= (char *) malloc (e
- value
+ 1);
768 strncpy (ss
, value
, e
- value
);
770 if (!strcmp (ff
, "date")) {
771 if (server_date
) free (server_date
);
774 if (document_date
) free (document_date
);
782 /* Done parsing the body. Now check the dates we (hopefully) parsed. */
783 if (!server_date
|| !*server_date
) {
784 xasprintf (msg
, _("%sServer date unknown, "), *msg
);
785 date_result
= max_state_alt(STATE_UNKNOWN
, date_result
);
786 } else if (!document_date
|| !*document_date
) {
787 xasprintf (msg
, _("%sDocument modification date unknown, "), *msg
);
788 date_result
= max_state_alt(STATE_CRITICAL
, date_result
);
790 time_t srv_data
= parse_time_string (server_date
);
791 time_t doc_data
= parse_time_string (document_date
);
794 xasprintf (msg
, _("%sServer date \"%100s\" unparsable, "), *msg
, server_date
);
795 date_result
= max_state_alt(STATE_CRITICAL
, date_result
);
796 } else if (doc_data
<= 0) {
797 xasprintf (msg
, _("%sDocument date \"%100s\" unparsable, "), *msg
, document_date
);
798 date_result
= max_state_alt(STATE_CRITICAL
, date_result
);
799 } else if (doc_data
> srv_data
+ 30) {
800 xasprintf (msg
, _("%sDocument is %d seconds in the future, "), *msg
, (int)doc_data
- (int)srv_data
);
801 date_result
= max_state_alt(STATE_CRITICAL
, date_result
);
802 } else if (doc_data
< srv_data
- maximum_age
) {
803 int n
= (srv_data
- doc_data
);
804 if (n
> (60 * 60 * 24 * 2)) {
805 xasprintf (msg
, _("%sLast modified %.1f days ago, "), *msg
, ((float) n
) / (60 * 60 * 24));
806 date_result
= max_state_alt(STATE_CRITICAL
, date_result
);
808 xasprintf (msg
, _("%sLast modified %d:%02d:%02d ago, "), *msg
, n
/ (60 * 60), (n
/ 60) % 60, n
% 60);
809 date_result
= max_state_alt(STATE_CRITICAL
, date_result
);
813 free (document_date
);
819 get_content_length (const char *headers
)
822 int content_length
= 0;
826 const char *field
= s
;
827 const char *value
= 0;
829 /* Find the end of the header field */
830 while (*s
&& !isspace(*s
) && *s
!= ':')
833 /* Remember the header value, if any. */
837 /* Skip to the end of the header, including continuation lines. */
838 while (*s
&& !(*s
== '\n' && (s
[1] != ' ' && s
[1] != '\t')))
841 /* Avoid stepping over end-of-string marker */
845 /* Process this header. */
846 if (value
&& value
> field
+2) {
847 char *ff
= (char *) malloc (value
-field
);
849 while (field
< value
-1)
850 *ss
++ = tolower(*field
++);
853 if (!strcmp (ff
, "content-length")) {
855 while (*value
&& isspace (*value
))
857 for (e
= value
; *e
&& *e
!= '\r' && *e
!= '\n'; e
++)
859 ss
= (char *) malloc (e
- value
+ 1);
860 strncpy (ss
, value
, e
- value
);
862 content_length
= atoi(ss
);
868 return (content_length
);
872 prepend_slash (char *path
)
879 if ((newpath
= malloc (strlen(path
) + 2)) == NULL
)
880 die (STATE_UNKNOWN
, _("HTTP UNKNOWN - Memory allocation error\n"));
882 strcpy (newpath
+ 1, path
);
904 double elapsed_time
= 0.0;
905 long microsec_connect
= 0L;
906 double elapsed_time_connect
= 0.0;
907 long microsec_ssl
= 0L;
908 double elapsed_time_ssl
= 0.0;
909 long microsec_firstbyte
= 0L;
910 double elapsed_time_firstbyte
= 0.0;
911 long microsec_headers
= 0L;
912 double elapsed_time_headers
= 0.0;
913 long microsec_transfer
= 0L;
914 double elapsed_time_transfer
= 0.0;
916 int result
= STATE_OK
;
917 char *force_host_header
= NULL
;
919 /* try to connect to the host at the given port number */
920 gettimeofday (&tv_temp
, NULL
);
921 if (my_tcp_connect (server_address
, server_port
, &sd
) != STATE_OK
)
922 die (STATE_CRITICAL
, _("HTTP CRITICAL - Unable to open TCP socket\n"));
923 microsec_connect
= deltime (tv_temp
);
925 /* if we are called with the -I option, the -j method is CONNECT and */
926 /* we received -S for SSL, then we tunnel the request through a proxy*/
927 /* @20100414, public[at]frank4dd.com, http://www.frank4dd.com/howto */
929 if ( server_address
!= NULL
&& strcmp(http_method
, "CONNECT") == 0
930 && host_name
!= NULL
&& use_ssl
== TRUE
) {
932 if (verbose
) printf ("Entering CONNECT tunnel mode with proxy %s:%d to dst %s:%d\n", server_address
, server_port
, host_name
, HTTPS_PORT
);
933 asprintf (&buf
, "%s %s:%d HTTP/1.1\r\n%s\r\n", http_method
, host_name
, HTTPS_PORT
, user_agent
);
934 asprintf (&buf
, "%sProxy-Connection: keep-alive\r\n", buf
);
935 asprintf (&buf
, "%sHost: %s\r\n", buf
, host_name
);
936 /* we finished our request, send empty line with CRLF */
937 asprintf (&buf
, "%s%s", buf
, CRLF
);
938 if (verbose
) printf ("%s\n", buf
);
939 send(sd
, buf
, strlen (buf
), 0);
942 if (verbose
) printf ("Receive response from proxy\n");
943 read (sd
, buffer
, MAX_INPUT_BUFFER
-1);
944 if (verbose
) printf ("%s", buffer
);
945 /* Here we should check if we got HTTP/1.1 200 Connection established */
948 elapsed_time_connect
= (double)microsec_connect
/ 1.0e6
;
949 if (use_ssl
== TRUE
) {
950 gettimeofday (&tv_temp
, NULL
);
951 result
= np_net_ssl_init_with_hostname_version_and_cert(sd
, (use_sni
? host_name
: NULL
), ssl_version
, client_cert
, client_privkey
);
952 if (verbose
) printf ("SSL initialized\n");
953 if (result
!= STATE_OK
)
954 die (STATE_CRITICAL
, NULL
);
955 microsec_ssl
= deltime (tv_temp
);
956 elapsed_time_ssl
= (double)microsec_ssl
/ 1.0e6
;
957 if (check_cert
== TRUE
) {
958 result
= np_net_ssl_check_cert(days_till_exp_warn
, days_till_exp_crit
);
960 np_net_ssl_cleanup();
964 #endif /* HAVE_SSL */
966 if ( server_address
!= NULL
&& strcmp(http_method
, "CONNECT") == 0
967 && host_name
!= NULL
&& use_ssl
== TRUE
)
968 asprintf (&buf
, "%s %s %s\r\n%s\r\n", http_method_proxy
, server_url
, host_name
? "HTTP/1.1" : "HTTP/1.0", user_agent
);
970 asprintf (&buf
, "%s %s %s\r\n%s\r\n", http_method
, server_url
, host_name
? "HTTP/1.1" : "HTTP/1.0", user_agent
);
972 /* tell HTTP/1.1 servers not to keep the connection alive */
973 xasprintf (&buf
, "%sConnection: close\r\n", buf
);
975 /* check if Host header is explicitly set in options */
976 if (http_opt_headers_count
) {
977 for (i
= 0; i
< http_opt_headers_count
; i
++) {
978 if (strncmp(http_opt_headers
[i
], "Host:", 5) == 0) {
979 force_host_header
= http_opt_headers
[i
];
984 /* optionally send the host header info */
986 if (force_host_header
) {
987 xasprintf (&buf
, "%s%s\r\n", buf
, force_host_header
);
991 * Specify the port only if we're using a non-default port (see RFC 2616,
992 * 14.23). Some server applications/configurations cause trouble if the
993 * (default) port is explicitly specified in the "Host:" header line.
995 if ((use_ssl
== FALSE
&& virtual_port
== HTTP_PORT
) ||
996 (use_ssl
== TRUE
&& virtual_port
== HTTPS_PORT
) ||
997 (server_address
!= NULL
&& strcmp(http_method
, "CONNECT") == 0
998 && host_name
!= NULL
&& use_ssl
== TRUE
))
999 xasprintf (&buf
, "%sHost: %s\r\n", buf
, host_name
);
1001 xasprintf (&buf
, "%sHost: %s:%d\r\n", buf
, host_name
, virtual_port
);
1005 /* optionally send any other header tag */
1006 if (http_opt_headers_count
) {
1007 for (i
= 0; i
< http_opt_headers_count
; i
++) {
1008 if (force_host_header
!= http_opt_headers
[i
]) {
1009 xasprintf (&buf
, "%s%s\r\n", buf
, http_opt_headers
[i
]);
1012 /* This cannot be free'd here because a redirection will then try to access this and segfault */
1013 /* Covered in a testcase in tests/check_http.t */
1014 /* free(http_opt_headers); */
1017 /* optionally send the authentication info */
1018 if (strlen(user_auth
)) {
1019 base64_encode_alloc (user_auth
, strlen (user_auth
), &auth
);
1020 xasprintf (&buf
, "%sAuthorization: Basic %s\r\n", buf
, auth
);
1023 /* optionally send the proxy authentication info */
1024 if (strlen(proxy_auth
)) {
1025 base64_encode_alloc (proxy_auth
, strlen (proxy_auth
), &auth
);
1026 xasprintf (&buf
, "%sProxy-Authorization: Basic %s\r\n", buf
, auth
);
1029 /* either send http POST data (any data, not only POST)*/
1030 if (http_post_data
) {
1031 if (http_content_type
) {
1032 xasprintf (&buf
, "%sContent-Type: %s\r\n", buf
, http_content_type
);
1034 xasprintf (&buf
, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf
);
1037 xasprintf (&buf
, "%sContent-Length: %i\r\n\r\n", buf
, (int)strlen (http_post_data
));
1038 xasprintf (&buf
, "%s%s%s", buf
, http_post_data
, CRLF
);
1041 /* or just a newline so the server knows we're done with the request */
1042 xasprintf (&buf
, "%s%s", buf
, CRLF
);
1045 if (verbose
) printf ("%s\n", buf
);
1046 gettimeofday (&tv_temp
, NULL
);
1047 my_send (buf
, strlen (buf
));
1048 microsec_headers
= deltime (tv_temp
);
1049 elapsed_time_headers
= (double)microsec_headers
/ 1.0e6
;
1051 /* fetch the page */
1052 full_page
= strdup("");
1053 gettimeofday (&tv_temp
, NULL
);
1054 while ((i
= my_recv (buffer
, MAX_INPUT_BUFFER
-1)) > 0) {
1055 if ((i
>= 1) && (elapsed_time_firstbyte
<= 0.000001)) {
1056 microsec_firstbyte
= deltime (tv_temp
);
1057 elapsed_time_firstbyte
= (double)microsec_firstbyte
/ 1.0e6
;
1059 while (pos
= memchr(buffer
, '\0', i
)) {
1060 /* replace nul character with a blank */
1064 xasprintf (&full_page_new
, "%s%s", full_page
, buffer
);
1066 full_page
= full_page_new
;
1069 if (no_body
&& document_headers_done (full_page
)) {
1074 microsec_transfer
= deltime (tv_temp
);
1075 elapsed_time_transfer
= (double)microsec_transfer
/ 1.0e6
;
1077 if (i
< 0 && errno
!= ECONNRESET
) {
1081 sslerr=SSL_get_error(ssl, i);
1082 if ( sslerr == SSL_ERROR_SSL ) {
1083 die (STATE_WARNING, _("HTTP WARNING - Client Certificate Required\n"));
1085 die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
1091 die (STATE_CRITICAL
, _("HTTP CRITICAL - Error on receive\n"));
1099 /* return a CRITICAL status if we couldn't read any data */
1100 if (pagesize
== (size_t) 0)
1101 die (STATE_CRITICAL
, _("HTTP CRITICAL - No data received from host\n"));
1103 /* close the connection */
1106 np_net_ssl_cleanup();
1109 /* Save check time */
1110 microsec
= deltime (tv
);
1111 elapsed_time
= (double)microsec
/ 1.0e6
;
1113 /* leave full_page untouched so we can free it later */
1117 printf ("%s://%s:%d%s is %d characters\n",
1118 use_ssl
? "https" : "http", server_address
,
1119 server_port
, server_url
, (int)pagesize
);
1121 /* find status line and null-terminate it */
1123 page
+= (size_t) strcspn (page
, "\r\n");
1125 page
+= (size_t) strspn (page
, "\r\n");
1126 status_line
[strcspn(status_line
, "\r\n")] = 0;
1127 strip (status_line
);
1129 printf ("STATUS: %s\n", status_line
);
1131 /* find header info and null-terminate it */
1133 while (strcspn (page
, "\r\n") > 0) {
1134 page
+= (size_t) strcspn (page
, "\r\n");
1136 if ((strspn (page
, "\r") == 1 && strspn (page
, "\r\n") >= 2) ||
1137 (strspn (page
, "\n") == 1 && strspn (page
, "\r\n") >= 2))
1142 page
+= (size_t) strspn (page
, "\r\n");
1143 header
[pos
- header
] = 0;
1145 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header
,
1146 (no_body
? " [[ skipped ]]" : page
));
1148 /* make sure the status line matches the response we are looking for */
1149 if (!expected_statuscode (status_line
, server_expect
)) {
1150 if (server_port
== HTTP_PORT
)
1152 _("Invalid HTTP response received from host: %s\n"),
1156 _("Invalid HTTP response received from host on port %d: %s\n"),
1157 server_port
, status_line
);
1159 xasprintf (&msg
, _("%s\n%s"), msg
, page
);
1160 die (STATE_CRITICAL
, "HTTP CRITICAL - %s", msg
);
1163 /* Bypass normal status line check if server_expect was set by user and not default */
1164 /* NOTE: After this if/else block msg *MUST* be an asprintf-allocated string */
1165 if ( server_expect_yn
) {
1167 _("Status line output matched \"%s\" - "), server_expect
);
1169 printf ("%s\n",msg
);
1172 /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
1173 /* HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
1174 /* Status-Code = 3 DIGITS */
1176 status_code
= strchr (status_line
, ' ') + sizeof (char);
1177 if (strspn (status_code
, "1234567890") != 3)
1178 die (STATE_CRITICAL
, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line
);
1180 http_status
= atoi (status_code
);
1182 /* check the return code */
1184 if (http_status
>= 600 || http_status
< 100) {
1185 die (STATE_CRITICAL
, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line
);
1187 /* server errors result in a critical state */
1188 else if (http_status
>= 500) {
1189 xasprintf (&msg
, _("%s - "), status_line
);
1190 result
= STATE_CRITICAL
;
1192 /* client errors result in a warning state */
1193 else if (http_status
>= 400) {
1194 xasprintf (&msg
, _("%s - "), status_line
);
1195 result
= max_state_alt(STATE_WARNING
, result
);
1197 /* check redirected page if specified */
1198 else if (http_status
>= 300) {
1200 if (onredirect
== STATE_DEPENDENT
)
1201 redir (header
, status_line
);
1203 result
= max_state_alt(onredirect
, result
);
1204 xasprintf (&msg
, _("%s - "), status_line
);
1205 } /* end if (http_status >= 300) */
1207 /* Print OK status anyway */
1208 xasprintf (&msg
, _("%s - "), status_line
);
1211 } /* end else (server_expect_yn) */
1213 /* reset the alarm - must be called *after* redir or we'll never die on redirects! */
1216 if (maximum_age
>= 0) {
1217 result
= max_state_alt(check_document_dates(header
, &msg
), result
);
1220 /* Page and Header content checks go here */
1221 if (strlen (header_expect
)) {
1222 if (!strstr (header
, header_expect
)) {
1223 strncpy(&output_header_search
[0],header_expect
,sizeof(output_header_search
));
1224 if(output_header_search
[sizeof(output_header_search
)-1]!='\0') {
1225 bcopy("...",&output_header_search
[sizeof(output_header_search
)-4],4);
1227 xasprintf (&msg
, _("%sheader '%s' not found on '%s://%s:%d%s', "), msg
, output_header_search
, use_ssl
? "https" : "http", host_name
? host_name
: server_address
, server_port
, server_url
);
1228 result
= STATE_CRITICAL
;
1233 if (strlen (string_expect
)) {
1234 if (!strstr (page
, string_expect
)) {
1235 strncpy(&output_string_search
[0],string_expect
,sizeof(output_string_search
));
1236 if(output_string_search
[sizeof(output_string_search
)-1]!='\0') {
1237 bcopy("...",&output_string_search
[sizeof(output_string_search
)-4],4);
1239 xasprintf (&msg
, _("%sstring '%s' not found on '%s://%s:%d%s', "), msg
, output_string_search
, use_ssl
? "https" : "http", host_name
? host_name
: server_address
, server_port
, server_url
);
1240 result
= STATE_CRITICAL
;
1244 if (strlen (regexp
)) {
1245 errcode
= regexec (&preg
, page
, REGS
, pmatch
, 0);
1246 if ((errcode
== 0 && invert_regex
== 0) || (errcode
== REG_NOMATCH
&& invert_regex
== 1)) {
1247 /* OK - No-op to avoid changing the logic around it */
1248 result
= max_state_alt(STATE_OK
, result
);
1250 else if ((errcode
== REG_NOMATCH
&& invert_regex
== 0) || (errcode
== 0 && invert_regex
== 1)) {
1251 if (invert_regex
== 0)
1252 xasprintf (&msg
, _("%spattern not found, "), msg
);
1254 xasprintf (&msg
, _("%spattern found, "), msg
);
1255 result
= STATE_CRITICAL
;
1258 /* FIXME: Shouldn't that be UNKNOWN? */
1259 regerror (errcode
, &preg
, errbuf
, MAX_INPUT_BUFFER
);
1260 xasprintf (&msg
, _("%sExecute Error: %s, "), msg
, errbuf
);
1261 result
= STATE_CRITICAL
;
1265 /* make sure the page is of an appropriate size */
1266 /* page_len = get_content_length(header); */
1267 /* FIXME: Will this work with -N ? IMHO we should use
1268 * get_content_length(header) and always check if it's different than the
1271 /* FIXME: IIRC pagesize returns headers - shouldn't we make
1272 * it == get_content_length(header) ??
1274 page_len
= pagesize
;
1275 if ((max_page_len
> 0) && (page_len
> max_page_len
)) {
1276 xasprintf (&msg
, _("%spage size %d too large, "), msg
, page_len
);
1277 result
= max_state_alt(STATE_WARNING
, result
);
1278 } else if ((min_page_len
> 0) && (page_len
< min_page_len
)) {
1279 xasprintf (&msg
, _("%spage size %d too small, "), msg
, page_len
);
1280 result
= max_state_alt(STATE_WARNING
, result
);
1283 /* Cut-off trailing characters */
1284 if(msg
[strlen(msg
)-2] == ',')
1285 msg
[strlen(msg
)-2] = '\0';
1287 msg
[strlen(msg
)-3] = '\0';
1289 /* check elapsed time */
1290 if (show_extended_perfdata
)
1292 _("%s - %d bytes in %.3f second response time %s|%s %s %s %s %s %s %s"),
1293 msg
, page_len
, elapsed_time
,
1294 (display_html
? "</A>" : ""),
1295 perfd_time (elapsed_time
),
1296 perfd_size (page_len
),
1297 perfd_time_connect (elapsed_time_connect
),
1298 use_ssl
== TRUE
? perfd_time_ssl (elapsed_time_ssl
) : "",
1299 perfd_time_headers (elapsed_time_headers
),
1300 perfd_time_firstbyte (elapsed_time_firstbyte
),
1301 perfd_time_transfer (elapsed_time_transfer
));
1304 _("%s - %d bytes in %.3f second response time %s|%s %s"),
1305 msg
, page_len
, elapsed_time
,
1306 (display_html
? "</A>" : ""),
1307 perfd_time (elapsed_time
),
1308 perfd_size (page_len
));
1311 xasprintf (&msg
, _("%s\n%s"), msg
, page
);
1313 result
= max_state_alt(get_status(elapsed_time
, thlds
), result
);
1315 die (result
, "HTTP %s: %s\n", state_text(result
), msg
);
1317 return STATE_UNKNOWN
;
1323 #define URI_HTTP "%5[HTPShtps]"
1324 #define URI_HOST "%255[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1325 #define URI_PORT "%6d" /* MAX_PORT's width is 5 chars, 6 to detect overflow */
1326 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1327 #define HD1 URI_HTTP "://" URI_HOST ":" URI_PORT "/" URI_PATH
1328 #define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH
1329 #define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT
1330 #define HD4 URI_HTTP "://" URI_HOST
1331 #define HD5 URI_PATH
1334 redir (char *pos
, char *status_line
)
1343 addr
= malloc (MAX_IPV4_HOSTLENGTH
+ 1);
1345 die (STATE_UNKNOWN
, _("HTTP UNKNOWN - Could not allocate addr\n"));
1347 memset(addr
, 0, MAX_IPV4_HOSTLENGTH
);
1348 url
= malloc (strcspn (pos
, "\r\n"));
1350 die (STATE_UNKNOWN
, _("HTTP UNKNOWN - Could not allocate URL\n"));
1353 sscanf (pos
, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx
, &i
);
1355 pos
+= (size_t) strcspn (pos
, "\r\n");
1356 pos
+= (size_t) strspn (pos
, "\r\n");
1357 if (strlen(pos
) == 0)
1359 _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"),
1360 status_line
, (display_html
? "</A>" : ""));
1365 pos
+= strspn (pos
, " \t");
1368 * RFC 2616 (4.2): ``Header fields can be extended over multiple lines by
1369 * preceding each extra line with at least one SP or HT.''
1371 for (; (i
= strspn (pos
, "\r\n")); pos
+= i
) {
1373 if (!(i
= strspn (pos
, " \t"))) {
1374 die (STATE_UNKNOWN
, _("HTTP UNKNOWN - Empty redirect location%s\n"),
1375 display_html
? "</A>" : "");
1379 url
= realloc (url
, strcspn (pos
, "\r\n") + 1);
1381 die (STATE_UNKNOWN
, _("HTTP UNKNOWN - Could not allocate URL\n"));
1383 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
1384 if (sscanf (pos
, HD1
, type
, addr
, &i
, url
) == 4) {
1385 url
= prepend_slash (url
);
1386 use_ssl
= server_type_check (type
);
1389 /* URI_HTTP URI_HOST URI_PATH */
1390 else if (sscanf (pos
, HD2
, type
, addr
, url
) == 3 ) {
1391 url
= prepend_slash (url
);
1392 use_ssl
= server_type_check (type
);
1393 i
= server_port_check (use_ssl
);
1396 /* URI_HTTP URI_HOST URI_PORT */
1397 else if (sscanf (pos
, HD3
, type
, addr
, &i
) == 3) {
1398 strcpy (url
, HTTP_URL
);
1399 use_ssl
= server_type_check (type
);
1402 /* URI_HTTP URI_HOST */
1403 else if (sscanf (pos
, HD4
, type
, addr
) == 2) {
1404 strcpy (url
, HTTP_URL
);
1405 use_ssl
= server_type_check (type
);
1406 i
= server_port_check (use_ssl
);
1410 else if (sscanf (pos
, HD5
, url
) == 1) {
1412 if ((url
[0] != '/')) {
1413 if ((x
= strrchr(server_url
, '/')))
1415 xasprintf (&url
, "%s/%s", server_url
, url
);
1418 strcpy (type
, server_type
);
1419 strcpy (addr
, host_name
? host_name
: server_address
);
1424 _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"),
1425 pos
, (display_html
? "</A>" : ""));
1430 } /* end while (pos) */
1432 if (++redir_depth
> max_depth
)
1434 _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
1435 max_depth
, type
, addr
, i
, url
, (display_html
? "</A>" : ""));
1437 if (server_port
==i
&&
1438 !strncmp(server_address
, addr
, MAX_IPV4_HOSTLENGTH
) &&
1439 (host_name
&& !strncmp(host_name
, addr
, MAX_IPV4_HOSTLENGTH
)) &&
1440 !strcmp(server_url
, url
))
1442 _("HTTP WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1443 type
, addr
, i
, url
, (display_html
? "</A>" : ""));
1445 strcpy (server_type
, type
);
1448 host_name
= strndup (addr
, MAX_IPV4_HOSTLENGTH
);
1450 if (!(followsticky
& STICKY_HOST
)) {
1451 free (server_address
);
1452 server_address
= strndup (addr
, MAX_IPV4_HOSTLENGTH
);
1454 if (!(followsticky
& STICKY_PORT
)) {
1461 if (server_port
> MAX_PORT
)
1463 _("HTTP UNKNOWN - Redirection to port above %d - %s://%s:%d%s%s\n"),
1464 MAX_PORT
, server_type
, server_address
, server_port
, server_url
,
1465 display_html
? "</A>" : "");
1467 /* reset virtual port */
1468 virtual_port
= server_port
;
1471 printf (_("Redirection to %s://%s:%d%s\n"), server_type
,
1472 host_name
? host_name
: server_address
, server_port
, server_url
);
1480 server_type_check (const char *type
)
1482 if (strcmp (type
, "https"))
1489 server_port_check (int ssl_flag
)
1497 char *perfd_time (double elapsed_time
)
1499 return fperfdata ("time", elapsed_time
, "s",
1500 thlds
->warning
?TRUE
:FALSE
, thlds
->warning
?thlds
->warning
->end
:0,
1501 thlds
->critical
?TRUE
:FALSE
, thlds
->critical
?thlds
->critical
->end
:0,
1502 TRUE
, 0, TRUE
, socket_timeout
);
1505 char *perfd_time_connect (double elapsed_time_connect
)
1507 return fperfdata ("time_connect", elapsed_time_connect
, "s", FALSE
, 0, FALSE
, 0, FALSE
, 0, TRUE
, socket_timeout
);
1510 char *perfd_time_ssl (double elapsed_time_ssl
)
1512 return fperfdata ("time_ssl", elapsed_time_ssl
, "s", FALSE
, 0, FALSE
, 0, FALSE
, 0, TRUE
, socket_timeout
);
1515 char *perfd_time_headers (double elapsed_time_headers
)
1517 return fperfdata ("time_headers", elapsed_time_headers
, "s", FALSE
, 0, FALSE
, 0, FALSE
, 0, TRUE
, socket_timeout
);
1520 char *perfd_time_firstbyte (double elapsed_time_firstbyte
)
1522 return fperfdata ("time_firstbyte", elapsed_time_firstbyte
, "s", FALSE
, 0, FALSE
, 0, FALSE
, 0, TRUE
, socket_timeout
);
1525 char *perfd_time_transfer (double elapsed_time_transfer
)
1527 return fperfdata ("time_transfer", elapsed_time_transfer
, "s", FALSE
, 0, FALSE
, 0, FALSE
, 0, TRUE
, socket_timeout
);
1530 char *perfd_size (int page_len
)
1532 return perfdata ("size", page_len
, "B",
1533 (min_page_len
>0?TRUE
:FALSE
), min_page_len
,
1534 (min_page_len
>0?TRUE
:FALSE
), 0,
1541 print_revision (progname
, NP_VERSION
);
1543 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1544 printf (COPYRIGHT
, copyright
, email
);
1546 printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
1547 printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
1548 printf ("%s\n", _("strings and regular expressions, check connection times, and report on"));
1549 printf ("%s\n", _("certificate expiration times."));
1555 printf (_("NOTE: One or both of -H and -I must be specified"));
1559 printf (UT_HELP_VRSN
);
1560 printf (UT_EXTRA_OPTS
);
1562 printf (" %s\n", "-H, --hostname=ADDRESS");
1563 printf (" %s\n", _("Host name argument for servers using host headers (virtual host)"));
1564 printf (" %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1565 printf (" %s\n", "-I, --IP-address=ADDRESS");
1566 printf (" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1567 printf (" %s\n", "-p, --port=INTEGER");
1568 printf (" %s", _("Port number (default: "));
1569 printf ("%d)\n", HTTP_PORT
);
1574 printf (" %s\n", "-S, --ssl=VERSION[+]");
1575 printf (" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents"));
1576 printf (" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,"));
1577 printf (" %s\n", _("1.2 = TLSv1.2). With a '+' suffix, newer versions are also accepted."));
1578 printf (" %s\n", "--sni");
1579 printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
1580 printf (" %s\n", "-C, --certificate=INTEGER[,INTEGER]");
1581 printf (" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
1582 printf (" %s\n", _("(when this option is used the URL is not checked.)"));
1583 printf (" %s\n", "-J, --client-cert=FILE");
1584 printf (" %s\n", _("Name of file that contains the client certificate (PEM format)"));
1585 printf (" %s\n", _("to be used in establishing the SSL session"));
1586 printf (" %s\n", "-K, --private-key=FILE");
1587 printf (" %s\n", _("Name of file containing the private key (PEM format)"));
1588 printf (" %s\n", _("matching the client certificate"));
1591 printf (" %s\n", "-e, --expect=STRING");
1592 printf (" %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1593 printf (" %s", _("the first (status) line of the server response (default: "));
1594 printf ("%s)\n", HTTP_EXPECT
);
1595 printf (" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1596 printf (" %s\n", "-d, --header-string=STRING");
1597 printf (" %s\n", _("String to expect in the response headers"));
1598 printf (" %s\n", "-s, --string=STRING");
1599 printf (" %s\n", _("String to expect in the content"));
1600 printf (" %s\n", "-u, --url=PATH");
1601 printf (" %s\n", _("URL to GET or POST (default: /)"));
1602 printf (" %s\n", "-P, --post=STRING");
1603 printf (" %s\n", _("URL encoded http POST data"));
1604 printf (" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT, CONNECT:POST)");
1605 printf (" %s\n", _("Set HTTP method."));
1606 printf (" %s\n", "-N, --no-body");
1607 printf (" %s\n", _("Don't wait for document body: stop reading after headers."));
1608 printf (" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
1609 printf (" %s\n", "-M, --max-age=SECONDS");
1610 printf (" %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
1611 printf (" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1612 printf (" %s\n", "-T, --content-type=STRING");
1613 printf (" %s\n", _("specify Content-Type header media type when POSTing\n"));
1615 printf (" %s\n", "-l, --linespan");
1616 printf (" %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1617 printf (" %s\n", "-r, --regex, --ereg=STRING");
1618 printf (" %s\n", _("Search page for regex STRING"));
1619 printf (" %s\n", "-R, --eregi=STRING");
1620 printf (" %s\n", _("Search page for case-insensitive regex STRING"));
1621 printf (" %s\n", "--invert-regex");
1622 printf (" %s\n", _("Return CRITICAL if found, OK if not\n"));
1624 printf (" %s\n", "-a, --authorization=AUTH_PAIR");
1625 printf (" %s\n", _("Username:password on sites with basic authentication"));
1626 printf (" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
1627 printf (" %s\n", _("Username:password on proxy-servers with basic authentication"));
1628 printf (" %s\n", "-A, --useragent=STRING");
1629 printf (" %s\n", _("String to be sent in http header as \"User Agent\""));
1630 printf (" %s\n", "-k, --header=STRING");
1631 printf (" %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers"));
1632 printf (" %s\n", "-E, --extended-perfdata");
1633 printf (" %s\n", _("Print additional performance data"));
1634 printf (" %s\n", "-B, --show-body");
1635 printf (" %s\n", _("Print body content below status line"));
1636 printf (" %s\n", "-L, --link");
1637 printf (" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1638 printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>");
1639 printf (" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1640 printf (" %s\n", _("specified IP address. stickyport also ensures port stays the same."));
1641 printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1642 printf (" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1644 printf (UT_WARN_CRIT
);
1646 printf (UT_CONN_TIMEOUT
, DEFAULT_SOCKET_TIMEOUT
);
1648 printf (UT_VERBOSE
);
1651 printf ("%s\n", _("Notes:"));
1652 printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1653 printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1654 printf (" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response"));
1655 printf (" %s\n", _("messages from the host result in STATE_WARNING return values. If you are"));
1656 printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1657 printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
1661 printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
1662 printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
1663 printf (" %s\n", _("certificate is still valid for the specified number of days."));
1665 printf (" %s\n", _("Please note that this plugin does not check if the presented server"));
1666 printf (" %s\n", _("certificate matches the hostname of the server, or if the certificate"));
1667 printf (" %s\n", _("has a valid chain of trust to one of the locally installed CAs."));
1669 printf ("%s\n", _("Examples:"));
1670 printf (" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com");
1671 printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
1672 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1673 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1674 printf (" %s\n", _("a STATE_CRITICAL will be returned."));
1676 printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 14");
1677 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1678 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1679 printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
1680 printf (" %s\n\n", _("the certificate is expired."));
1682 printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 30,14");
1683 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 30 days,"));
1684 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1685 printf (" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned."));
1686 printf (" %s\n", _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days"));
1688 printf (" %s\n\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: ");
1689 printf (" %s\n", _("check_http -I 192.168.100.35 -p 80 -u https://www.verisign.com/ -S -j CONNECT -H www.verisign.com "));
1690 printf (" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> -S(sl) -j CONNECT -H <webserver>"));
1691 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1692 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1693 printf (" %s\n", _("a STATE_CRITICAL will be returned. By adding a colon to the method you can set the method used"));
1694 printf (" %s\n", _("inside the proxied connection: -j CONNECT:POST"));
1698 printf (UT_SUPPORT
);
1707 printf ("%s\n", _("Usage:"));
1708 printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname
);
1709 printf (" [-J <client certificate file>] [-K <private key>]\n");
1710 printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n");
1711 printf (" [-b proxy_auth] [-f <ok|warning|critcal|follow|sticky|stickyport>]\n");
1712 printf (" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
1713 printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
1714 printf (" [-A string] [-k string] [-S <version>] [--sni] [-C <warn_age>[,<crit_age>]]\n");
1715 printf (" [-T <content-type>] [-j method]\n");