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
;
124 int min_page_len
= 0;
125 int max_page_len
= 0;
129 char *http_post_data
;
130 char *http_content_type
;
131 char buffer
[MAX_INPUT_BUFFER
];
132 char *client_cert
= NULL
;
133 char *client_privkey
= NULL
;
135 int process_arguments (int, char **);
136 int check_http (void);
137 void redir (char *pos
, char *status_line
);
138 int server_type_check(const char *type
);
139 int server_port_check(int ssl_flag
);
140 char *perfd_time (double microsec
);
141 char *perfd_time_connect (double microsec
);
142 char *perfd_time_ssl (double microsec
);
143 char *perfd_time_firstbyte (double microsec
);
144 char *perfd_time_headers (double microsec
);
145 char *perfd_time_transfer (double microsec
);
146 char *perfd_size (int page_len
);
147 void print_help (void);
148 void print_usage (void);
151 main (int argc
, char **argv
)
153 int result
= STATE_UNKNOWN
;
155 setlocale (LC_ALL
, "");
156 bindtextdomain (PACKAGE
, LOCALEDIR
);
157 textdomain (PACKAGE
);
159 /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
160 server_url
= strdup(HTTP_URL
);
161 server_url_length
= strlen(server_url
);
162 xasprintf (&user_agent
, "User-Agent: check_http/v%s (monitoring-plugins %s)",
163 NP_VERSION
, VERSION
);
165 /* Parse extra opts if any */
166 argv
=np_extra_opts (&argc
, argv
, progname
);
168 if (process_arguments (argc
, argv
) == ERROR
)
169 usage4 (_("Could not parse arguments"));
171 if (display_html
== TRUE
)
172 printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">",
173 use_ssl
? "https" : "http", host_name
? host_name
: server_address
,
174 server_port
, server_url
);
176 /* initialize alarm signal handling, set socket timeout, start timer */
177 (void) signal (SIGALRM
, socket_timeout_alarm_handler
);
178 (void) alarm (socket_timeout
);
179 gettimeofday (&tv
, NULL
);
181 result
= check_http ();
185 /* check whether a file exists */
187 test_file (char *path
)
189 if (access(path
, R_OK
) == 0)
191 usage2 (_("file does not exist or is not readable"), path
);
194 /* process command-line arguments */
196 process_arguments (int argc
, char **argv
)
203 INVERT_REGEX
= CHAR_MAX
+ 1,
208 static struct option longopts
[] = {
210 {"link", no_argument
, 0, 'L'},
211 {"nohtml", no_argument
, 0, 'n'},
212 {"ssl", optional_argument
, 0, 'S'},
213 {"sni", no_argument
, 0, SNI_OPTION
},
214 {"post", required_argument
, 0, 'P'},
215 {"method", required_argument
, 0, 'j'},
216 {"IP-address", required_argument
, 0, 'I'},
217 {"url", required_argument
, 0, 'u'},
218 {"port", required_argument
, 0, 'p'},
219 {"authorization", required_argument
, 0, 'a'},
220 {"proxy-authorization", required_argument
, 0, 'b'},
221 {"header-string", required_argument
, 0, 'd'},
222 {"string", required_argument
, 0, 's'},
223 {"expect", required_argument
, 0, 'e'},
224 {"regex", required_argument
, 0, 'r'},
225 {"ereg", required_argument
, 0, 'r'},
226 {"eregi", required_argument
, 0, 'R'},
227 {"linespan", no_argument
, 0, 'l'},
228 {"onredirect", required_argument
, 0, 'f'},
229 {"certificate", required_argument
, 0, 'C'},
230 {"client-cert", required_argument
, 0, 'J'},
231 {"private-key", required_argument
, 0, 'K'},
232 {"useragent", required_argument
, 0, 'A'},
233 {"header", required_argument
, 0, 'k'},
234 {"no-body", no_argument
, 0, 'N'},
235 {"max-age", required_argument
, 0, 'M'},
236 {"content-type", required_argument
, 0, 'T'},
237 {"pagesize", required_argument
, 0, 'm'},
238 {"invert-regex", no_argument
, NULL
, INVERT_REGEX
},
239 {"use-ipv4", no_argument
, 0, '4'},
240 {"use-ipv6", no_argument
, 0, '6'},
241 {"extended-perfdata", no_argument
, 0, 'E'},
248 for (c
= 1; c
< argc
; c
++) {
249 if (strcmp ("-to", argv
[c
]) == 0)
250 strcpy (argv
[c
], "-t");
251 if (strcmp ("-hn", argv
[c
]) == 0)
252 strcpy (argv
[c
], "-H");
253 if (strcmp ("-wt", argv
[c
]) == 0)
254 strcpy (argv
[c
], "-w");
255 if (strcmp ("-ct", argv
[c
]) == 0)
256 strcpy (argv
[c
], "-c");
257 if (strcmp ("-nohtml", argv
[c
]) == 0)
258 strcpy (argv
[c
], "-n");
262 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:NE", longopts
, &option
);
263 if (c
== -1 || c
== EOF
)
267 case '?': /* usage */
272 exit (STATE_UNKNOWN
);
274 case 'V': /* version */
275 print_revision (progname
, NP_VERSION
);
276 exit (STATE_UNKNOWN
);
278 case 't': /* timeout period */
279 if (!is_intnonneg (optarg
))
280 usage2 (_("Timeout interval must be a positive integer"), optarg
);
282 socket_timeout
= atoi (optarg
);
284 case 'c': /* critical time threshold */
285 critical_thresholds
= optarg
;
287 case 'w': /* warning time threshold */
288 warning_thresholds
= optarg
;
290 case 'A': /* User Agent String */
291 xasprintf (&user_agent
, "User-Agent: %s", optarg
);
293 case 'k': /* Additional headers */
294 if (http_opt_headers_count
== 0)
295 http_opt_headers
= malloc (sizeof (char *) * (++http_opt_headers_count
));
297 http_opt_headers
= realloc (http_opt_headers
, sizeof (char *) * (++http_opt_headers_count
));
298 http_opt_headers
[http_opt_headers_count
- 1] = optarg
;
299 /* xasprintf (&http_opt_headers, "%s", optarg); */
301 case 'L': /* show html link */
304 case 'n': /* do not show html link */
305 display_html
= FALSE
;
307 case 'C': /* Check SSL cert validity */
309 if ((temp
=strchr(optarg
,','))!=NULL
) {
311 if (!is_intnonneg (optarg
))
312 usage2 (_("Invalid certificate expiration period"), optarg
);
313 days_till_exp_warn
= atoi(optarg
);
316 if (!is_intnonneg (temp
))
317 usage2 (_("Invalid certificate expiration period"), temp
);
318 days_till_exp_crit
= atoi (temp
);
321 days_till_exp_crit
=0;
322 if (!is_intnonneg (optarg
))
323 usage2 (_("Invalid certificate expiration period"), optarg
);
324 days_till_exp_warn
= atoi (optarg
);
329 case 'J': /* use client certificate */
332 client_cert
= optarg
;
335 case 'K': /* use client private key */
338 client_privkey
= optarg
;
341 case 'S': /* use SSL */
344 /* ssl_version initialized to 0 as a default. Only set if it's non-zero. This helps when we include multiple
345 parameters, like -S and -C combinations */
347 if (c
=='S' && optarg
!= NULL
) {
348 int got_plus
= strchr(optarg
, '+') != NULL
;
350 if (!strncmp (optarg
, "1.2", 3))
351 ssl_version
= got_plus
? MP_TLSv1_2_OR_NEWER
: MP_TLSv1_2
;
352 else if (!strncmp (optarg
, "1.1", 3))
353 ssl_version
= got_plus
? MP_TLSv1_1_OR_NEWER
: MP_TLSv1_1
;
354 else if (optarg
[0] == '1')
355 ssl_version
= got_plus
? MP_TLSv1_OR_NEWER
: MP_TLSv1
;
356 else if (optarg
[0] == '3')
357 ssl_version
= got_plus
? MP_SSLv3_OR_NEWER
: MP_SSLv3
;
358 else if (optarg
[0] == '2')
359 ssl_version
= got_plus
? MP_SSLv2_OR_NEWER
: MP_SSLv2
;
361 usage4 (_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2 (with optional '+' suffix)"));
363 if (specify_port
== FALSE
)
364 server_port
= HTTPS_PORT
;
366 /* -C -J and -K fall through to here without SSL */
367 usage4 (_("Invalid option - SSL is not available"));
373 case 'f': /* onredirect */
374 if (!strcmp (optarg
, "stickyport"))
375 onredirect
= STATE_DEPENDENT
, followsticky
= STICKY_HOST
|STICKY_PORT
;
376 else if (!strcmp (optarg
, "sticky"))
377 onredirect
= STATE_DEPENDENT
, followsticky
= STICKY_HOST
;
378 else if (!strcmp (optarg
, "follow"))
379 onredirect
= STATE_DEPENDENT
, followsticky
= STICKY_NONE
;
380 else if (!strcmp (optarg
, "unknown"))
381 onredirect
= STATE_UNKNOWN
;
382 else if (!strcmp (optarg
, "ok"))
383 onredirect
= STATE_OK
;
384 else if (!strcmp (optarg
, "warning"))
385 onredirect
= STATE_WARNING
;
386 else if (!strcmp (optarg
, "critical"))
387 onredirect
= STATE_CRITICAL
;
388 else usage2 (_("Invalid onredirect option"), optarg
);
390 printf(_("option f:%d \n"), onredirect
);
392 /* Note: H, I, and u must be malloc'd or will fail on redirects */
393 case 'H': /* Host Name (virtual host) */
394 host_name
= strdup (optarg
);
395 if (host_name
[0] == '[') {
396 if ((p
= strstr (host_name
, "]:")) != NULL
) { /* [IPv6]:port */
397 virtual_port
= atoi (p
+ 2);
398 /* cut off the port */
399 host_name_length
= strlen (host_name
) - strlen (p
) - 1;
401 host_name
= strndup (optarg
, host_name_length
);
402 if (specify_port
== FALSE
)
403 server_port
= virtual_port
;
405 } else if ((p
= strchr (host_name
, ':')) != NULL
406 && strchr (++p
, ':') == NULL
) { /* IPv4:port or host:port */
407 virtual_port
= atoi (p
);
408 /* cut off the port */
409 host_name_length
= strlen (host_name
) - strlen (p
) - 1;
411 host_name
= strndup (optarg
, host_name_length
);
412 if (specify_port
== FALSE
)
413 server_port
= virtual_port
;
416 case 'I': /* Server IP-address */
417 server_address
= strdup (optarg
);
419 case 'u': /* URL path */
420 server_url
= strdup (optarg
);
421 server_url_length
= strlen (server_url
);
423 case 'p': /* Server port */
424 if (!is_intnonneg (optarg
))
425 usage2 (_("Invalid port number"), optarg
);
427 server_port
= atoi (optarg
);
431 case 'a': /* authorization info */
432 strncpy (user_auth
, optarg
, MAX_INPUT_BUFFER
- 1);
433 user_auth
[MAX_INPUT_BUFFER
- 1] = 0;
435 case 'b': /* proxy-authorization info */
436 strncpy (proxy_auth
, optarg
, MAX_INPUT_BUFFER
- 1);
437 proxy_auth
[MAX_INPUT_BUFFER
- 1] = 0;
439 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
440 if (! http_post_data
)
441 http_post_data
= strdup (optarg
);
443 http_method
= strdup("POST");
445 case 'j': /* Set HTTP method */
448 http_method
= strdup (optarg
);
450 case 'd': /* string or substring */
451 strncpy (header_expect
, optarg
, MAX_INPUT_BUFFER
- 1);
452 header_expect
[MAX_INPUT_BUFFER
- 1] = 0;
454 case 's': /* string or substring */
455 strncpy (string_expect
, optarg
, MAX_INPUT_BUFFER
- 1);
456 string_expect
[MAX_INPUT_BUFFER
- 1] = 0;
458 case 'e': /* string or substring */
459 strncpy (server_expect
, optarg
, MAX_INPUT_BUFFER
- 1);
460 server_expect
[MAX_INPUT_BUFFER
- 1] = 0;
461 server_expect_yn
= 1;
463 case 'T': /* Content-type */
464 xasprintf (&http_content_type
, "%s", optarg
);
466 case 'l': /* linespan */
467 cflags
&= ~REG_NEWLINE
;
469 case 'R': /* regex */
471 case 'r': /* regex */
472 strncpy (regexp
, optarg
, MAX_RE_SIZE
- 1);
473 regexp
[MAX_RE_SIZE
- 1] = 0;
474 errcode
= regcomp (&preg
, regexp
, cflags
);
476 (void) regerror (errcode
, &preg
, errbuf
, MAX_INPUT_BUFFER
);
477 printf (_("Could Not Compile Regular Expression: %s"), errbuf
);
485 address_family
= AF_INET
;
489 address_family
= AF_INET6
;
491 usage4 (_("IPv6 support not available"));
494 case 'v': /* verbose */
497 case 'm': /* min_page_length */
500 if (strchr(optarg
, ':') != (char *)NULL
) {
501 /* range, so get two values, min:max */
502 tmp
= strtok(optarg
, ":");
504 printf("Bad format: try \"-m min:max\"\n");
505 exit (STATE_WARNING
);
507 min_page_len
= atoi(tmp
);
509 tmp
= strtok(NULL
, ":");
511 printf("Bad format: try \"-m min:max\"\n");
512 exit (STATE_WARNING
);
514 max_page_len
= atoi(tmp
);
516 min_page_len
= atoi (optarg
);
519 case 'N': /* no-body */
522 case 'M': /* max-age */
524 int L
= strlen(optarg
);
525 if (L
&& optarg
[L
-1] == 'm')
526 maximum_age
= atoi (optarg
) * 60;
527 else if (L
&& optarg
[L
-1] == 'h')
528 maximum_age
= atoi (optarg
) * 60 * 60;
529 else if (L
&& optarg
[L
-1] == 'd')
530 maximum_age
= atoi (optarg
) * 60 * 60 * 24;
531 else if (L
&& (optarg
[L
-1] == 's' ||
532 isdigit (optarg
[L
-1])))
533 maximum_age
= atoi (optarg
);
535 fprintf (stderr
, "unparsable max-age: %s\n", optarg
);
536 exit (STATE_WARNING
);
540 case 'E': /* show extended perfdata */
541 show_extended_perfdata
= TRUE
;
548 if (server_address
== NULL
&& c
< argc
)
549 server_address
= strdup (argv
[c
++]);
551 if (host_name
== NULL
&& c
< argc
)
552 host_name
= strdup (argv
[c
++]);
554 if (server_address
== NULL
) {
555 if (host_name
== NULL
)
556 usage4 (_("You must specify a server address or host name"));
558 server_address
= strdup (host_name
);
561 set_thresholds(&thlds
, warning_thresholds
, critical_thresholds
);
563 if (critical_thresholds
&& thlds
->critical
->end
>(double)socket_timeout
)
564 socket_timeout
= (int)thlds
->critical
->end
+ 1;
566 if (http_method
== NULL
)
567 http_method
= strdup ("GET");
569 if (client_cert
&& !client_privkey
)
570 usage4 (_("If you use a client certificate you must also specify a private key file"));
572 if (virtual_port
== 0)
573 virtual_port
= server_port
;
580 /* Returns 1 if we're done processing the document body; 0 to keep going */
582 document_headers_done (char *full_page
)
586 for (body
= full_page
; *body
; body
++) {
587 if (!strncmp (body
, "\n\n", 2) || !strncmp (body
, "\n\r\n", 3))
592 return 0; /* haven't read end of headers yet */
594 full_page
[body
- full_page
] = 0;
599 parse_time_string (const char *string
)
603 memset (&tm
, 0, sizeof(tm
));
605 /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */
607 if (isupper (string
[0]) && /* Tue */
608 islower (string
[1]) &&
609 islower (string
[2]) &&
612 (isdigit(string
[5]) || string
[5] == ' ') && /* 25 */
613 isdigit (string
[6]) &&
615 isupper (string
[8]) && /* Dec */
616 islower (string
[9]) &&
617 islower (string
[10]) &&
619 isdigit (string
[12]) && /* 2001 */
620 isdigit (string
[13]) &&
621 isdigit (string
[14]) &&
622 isdigit (string
[15]) &&
624 isdigit (string
[17]) && /* 02: */
625 isdigit (string
[18]) &&
627 isdigit (string
[20]) && /* 59: */
628 isdigit (string
[21]) &&
630 isdigit (string
[23]) && /* 03 */
631 isdigit (string
[24]) &&
633 'G' == string
[26] && /* GMT */
634 'M' == string
[27] && /* GMT */
637 tm
.tm_sec
= 10 * (string
[23]-'0') + (string
[24]-'0');
638 tm
.tm_min
= 10 * (string
[20]-'0') + (string
[21]-'0');
639 tm
.tm_hour
= 10 * (string
[17]-'0') + (string
[18]-'0');
640 tm
.tm_mday
= 10 * (string
[5] == ' ' ? 0 : string
[5]-'0') + (string
[6]-'0');
641 tm
.tm_mon
= (!strncmp (string
+8, "Jan", 3) ? 0 :
642 !strncmp (string
+8, "Feb", 3) ? 1 :
643 !strncmp (string
+8, "Mar", 3) ? 2 :
644 !strncmp (string
+8, "Apr", 3) ? 3 :
645 !strncmp (string
+8, "May", 3) ? 4 :
646 !strncmp (string
+8, "Jun", 3) ? 5 :
647 !strncmp (string
+8, "Jul", 3) ? 6 :
648 !strncmp (string
+8, "Aug", 3) ? 7 :
649 !strncmp (string
+8, "Sep", 3) ? 8 :
650 !strncmp (string
+8, "Oct", 3) ? 9 :
651 !strncmp (string
+8, "Nov", 3) ? 10 :
652 !strncmp (string
+8, "Dec", 3) ? 11 :
654 tm
.tm_year
= ((1000 * (string
[12]-'0') +
655 100 * (string
[13]-'0') +
656 10 * (string
[14]-'0') +
660 tm
.tm_isdst
= 0; /* GMT is never in DST, right? */
662 if (tm
.tm_mon
< 0 || tm
.tm_mday
< 1 || tm
.tm_mday
> 31)
666 This is actually wrong: we need to subtract the local timezone
667 offset from GMT from this value. But, that's ok in this usage,
668 because we only comparing these two GMT dates against each other,
669 so it doesn't matter what time zone we parse them in.
673 if (t
== (time_t) -1) t
= 0;
676 const char *s
= string
;
677 while (*s
&& *s
!= '\r' && *s
!= '\n')
678 fputc (*s
++, stdout
);
679 printf (" ==> %lu\n", (unsigned long) t
);
689 /* Checks if the server 'reply' is one of the expected 'statuscodes' */
691 expected_statuscode (const char *reply
, const char *statuscodes
)
693 char *expected
, *code
;
696 if ((expected
= strdup (statuscodes
)) == NULL
)
697 die (STATE_UNKNOWN
, _("HTTP UNKNOWN - Memory allocation error\n"));
699 for (code
= strtok (expected
, ","); code
!= NULL
; code
= strtok (NULL
, ","))
700 if (strstr (reply
, code
) != NULL
) {
710 check_document_dates (const char *headers
, char **msg
)
713 char *server_date
= 0;
714 char *document_date
= 0;
715 int date_result
= STATE_OK
;
719 const char *field
= s
;
720 const char *value
= 0;
722 /* Find the end of the header field */
723 while (*s
&& !isspace(*s
) && *s
!= ':')
726 /* Remember the header value, if any. */
730 /* Skip to the end of the header, including continuation lines. */
731 while (*s
&& !(*s
== '\n' && (s
[1] != ' ' && s
[1] != '\t')))
734 /* Avoid stepping over end-of-string marker */
738 /* Process this header. */
739 if (value
&& value
> field
+2) {
740 char *ff
= (char *) malloc (value
-field
);
742 while (field
< value
-1)
743 *ss
++ = tolower(*field
++);
746 if (!strcmp (ff
, "date") || !strcmp (ff
, "last-modified")) {
748 while (*value
&& isspace (*value
))
750 for (e
= value
; *e
&& *e
!= '\r' && *e
!= '\n'; e
++)
752 ss
= (char *) malloc (e
- value
+ 1);
753 strncpy (ss
, value
, e
- value
);
755 if (!strcmp (ff
, "date")) {
756 if (server_date
) free (server_date
);
759 if (document_date
) free (document_date
);
767 /* Done parsing the body. Now check the dates we (hopefully) parsed. */
768 if (!server_date
|| !*server_date
) {
769 xasprintf (msg
, _("%sServer date unknown, "), *msg
);
770 date_result
= max_state_alt(STATE_UNKNOWN
, date_result
);
771 } else if (!document_date
|| !*document_date
) {
772 xasprintf (msg
, _("%sDocument modification date unknown, "), *msg
);
773 date_result
= max_state_alt(STATE_CRITICAL
, date_result
);
775 time_t srv_data
= parse_time_string (server_date
);
776 time_t doc_data
= parse_time_string (document_date
);
779 xasprintf (msg
, _("%sServer date \"%100s\" unparsable, "), *msg
, server_date
);
780 date_result
= max_state_alt(STATE_CRITICAL
, date_result
);
781 } else if (doc_data
<= 0) {
782 xasprintf (msg
, _("%sDocument date \"%100s\" unparsable, "), *msg
, document_date
);
783 date_result
= max_state_alt(STATE_CRITICAL
, date_result
);
784 } else if (doc_data
> srv_data
+ 30) {
785 xasprintf (msg
, _("%sDocument is %d seconds in the future, "), *msg
, (int)doc_data
- (int)srv_data
);
786 date_result
= max_state_alt(STATE_CRITICAL
, date_result
);
787 } else if (doc_data
< srv_data
- maximum_age
) {
788 int n
= (srv_data
- doc_data
);
789 if (n
> (60 * 60 * 24 * 2)) {
790 xasprintf (msg
, _("%sLast modified %.1f days ago, "), *msg
, ((float) n
) / (60 * 60 * 24));
791 date_result
= max_state_alt(STATE_CRITICAL
, date_result
);
793 xasprintf (msg
, _("%sLast modified %d:%02d:%02d ago, "), *msg
, n
/ (60 * 60), (n
/ 60) % 60, n
% 60);
794 date_result
= max_state_alt(STATE_CRITICAL
, date_result
);
798 free (document_date
);
804 get_content_length (const char *headers
)
807 int content_length
= 0;
811 const char *field
= s
;
812 const char *value
= 0;
814 /* Find the end of the header field */
815 while (*s
&& !isspace(*s
) && *s
!= ':')
818 /* Remember the header value, if any. */
822 /* Skip to the end of the header, including continuation lines. */
823 while (*s
&& !(*s
== '\n' && (s
[1] != ' ' && s
[1] != '\t')))
826 /* Avoid stepping over end-of-string marker */
830 /* Process this header. */
831 if (value
&& value
> field
+2) {
832 char *ff
= (char *) malloc (value
-field
);
834 while (field
< value
-1)
835 *ss
++ = tolower(*field
++);
838 if (!strcmp (ff
, "content-length")) {
840 while (*value
&& isspace (*value
))
842 for (e
= value
; *e
&& *e
!= '\r' && *e
!= '\n'; e
++)
844 ss
= (char *) malloc (e
- value
+ 1);
845 strncpy (ss
, value
, e
- value
);
847 content_length
= atoi(ss
);
853 return (content_length
);
857 prepend_slash (char *path
)
864 if ((newpath
= malloc (strlen(path
) + 2)) == NULL
)
865 die (STATE_UNKNOWN
, _("HTTP UNKNOWN - Memory allocation error\n"));
867 strcpy (newpath
+ 1, path
);
889 double elapsed_time
= 0.0;
890 long microsec_connect
= 0L;
891 double elapsed_time_connect
= 0.0;
892 long microsec_ssl
= 0L;
893 double elapsed_time_ssl
= 0.0;
894 long microsec_firstbyte
= 0L;
895 double elapsed_time_firstbyte
= 0.0;
896 long microsec_headers
= 0L;
897 double elapsed_time_headers
= 0.0;
898 long microsec_transfer
= 0L;
899 double elapsed_time_transfer
= 0.0;
901 int result
= STATE_OK
;
902 char *force_host_header
= NULL
;
904 /* try to connect to the host at the given port number */
905 gettimeofday (&tv_temp
, NULL
);
906 if (my_tcp_connect (server_address
, server_port
, &sd
) != STATE_OK
)
907 die (STATE_CRITICAL
, _("HTTP CRITICAL - Unable to open TCP socket\n"));
908 microsec_connect
= deltime (tv_temp
);
910 /* if we are called with the -I option, the -j method is CONNECT and */
911 /* we received -S for SSL, then we tunnel the request through a proxy*/
912 /* @20100414, public[at]frank4dd.com, http://www.frank4dd.com/howto */
914 if ( server_address
!= NULL
&& strcmp(http_method
, "CONNECT") == 0
915 && host_name
!= NULL
&& use_ssl
== TRUE
) {
917 if (verbose
) printf ("Entering CONNECT tunnel mode with proxy %s:%d to dst %s:%d\n", server_address
, server_port
, host_name
, HTTPS_PORT
);
918 asprintf (&buf
, "%s %s:%d HTTP/1.1\r\n%s\r\n", http_method
, host_name
, HTTPS_PORT
, user_agent
);
919 asprintf (&buf
, "%sProxy-Connection: keep-alive\r\n", buf
);
920 asprintf (&buf
, "%sHost: %s\r\n", buf
, host_name
);
921 /* we finished our request, send empty line with CRLF */
922 asprintf (&buf
, "%s%s", buf
, CRLF
);
923 if (verbose
) printf ("%s\n", buf
);
924 send(sd
, buf
, strlen (buf
), 0);
927 if (verbose
) printf ("Receive response from proxy\n");
928 read (sd
, buffer
, MAX_INPUT_BUFFER
-1);
929 if (verbose
) printf ("%s", buffer
);
930 /* Here we should check if we got HTTP/1.1 200 Connection established */
933 elapsed_time_connect
= (double)microsec_connect
/ 1.0e6
;
934 if (use_ssl
== TRUE
) {
935 gettimeofday (&tv_temp
, NULL
);
936 result
= np_net_ssl_init_with_hostname_version_and_cert(sd
, (use_sni
? host_name
: NULL
), ssl_version
, client_cert
, client_privkey
);
937 if (verbose
) printf ("SSL initialized\n");
938 if (result
!= STATE_OK
)
939 die (STATE_CRITICAL
, NULL
);
940 microsec_ssl
= deltime (tv_temp
);
941 elapsed_time_ssl
= (double)microsec_ssl
/ 1.0e6
;
942 if (check_cert
== TRUE
) {
943 result
= np_net_ssl_check_cert(days_till_exp_warn
, days_till_exp_crit
);
944 np_net_ssl_cleanup();
949 #endif /* HAVE_SSL */
951 if ( server_address
!= NULL
&& strcmp(http_method
, "CONNECT") == 0
952 && host_name
!= NULL
&& use_ssl
== TRUE
)
953 asprintf (&buf
, "%s %s %s\r\n%s\r\n", "GET", server_url
, host_name
? "HTTP/1.1" : "HTTP/1.0", user_agent
);
955 asprintf (&buf
, "%s %s %s\r\n%s\r\n", http_method
, server_url
, host_name
? "HTTP/1.1" : "HTTP/1.0", user_agent
);
957 /* tell HTTP/1.1 servers not to keep the connection alive */
958 xasprintf (&buf
, "%sConnection: close\r\n", buf
);
960 /* check if Host header is explicitly set in options */
961 if (http_opt_headers_count
) {
962 for (i
= 0; i
< http_opt_headers_count
; i
++) {
963 if (strncmp(http_opt_headers
[i
], "Host:", 5) == 0) {
964 force_host_header
= http_opt_headers
[i
];
969 /* optionally send the host header info */
971 if (force_host_header
) {
972 xasprintf (&buf
, "%s%s\r\n", buf
, force_host_header
);
976 * Specify the port only if we're using a non-default port (see RFC 2616,
977 * 14.23). Some server applications/configurations cause trouble if the
978 * (default) port is explicitly specified in the "Host:" header line.
980 if ((use_ssl
== FALSE
&& virtual_port
== HTTP_PORT
) ||
981 (use_ssl
== TRUE
&& virtual_port
== HTTPS_PORT
) ||
982 (server_address
!= NULL
&& strcmp(http_method
, "CONNECT") == 0
983 && host_name
!= NULL
&& use_ssl
== TRUE
))
984 xasprintf (&buf
, "%sHost: %s\r\n", buf
, host_name
);
986 xasprintf (&buf
, "%sHost: %s:%d\r\n", buf
, host_name
, virtual_port
);
990 /* optionally send any other header tag */
991 if (http_opt_headers_count
) {
992 for (i
= 0; i
< http_opt_headers_count
; i
++) {
993 if (force_host_header
!= http_opt_headers
[i
]) {
994 xasprintf (&buf
, "%s%s\r\n", buf
, http_opt_headers
[i
]);
997 /* This cannot be free'd here because a redirection will then try to access this and segfault */
998 /* Covered in a testcase in tests/check_http.t */
999 /* free(http_opt_headers); */
1002 /* optionally send the authentication info */
1003 if (strlen(user_auth
)) {
1004 base64_encode_alloc (user_auth
, strlen (user_auth
), &auth
);
1005 xasprintf (&buf
, "%sAuthorization: Basic %s\r\n", buf
, auth
);
1008 /* optionally send the proxy authentication info */
1009 if (strlen(proxy_auth
)) {
1010 base64_encode_alloc (proxy_auth
, strlen (proxy_auth
), &auth
);
1011 xasprintf (&buf
, "%sProxy-Authorization: Basic %s\r\n", buf
, auth
);
1014 /* either send http POST data (any data, not only POST)*/
1015 if (http_post_data
) {
1016 if (http_content_type
) {
1017 xasprintf (&buf
, "%sContent-Type: %s\r\n", buf
, http_content_type
);
1019 xasprintf (&buf
, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf
);
1022 xasprintf (&buf
, "%sContent-Length: %i\r\n\r\n", buf
, (int)strlen (http_post_data
));
1023 xasprintf (&buf
, "%s%s%s", buf
, http_post_data
, CRLF
);
1026 /* or just a newline so the server knows we're done with the request */
1027 xasprintf (&buf
, "%s%s", buf
, CRLF
);
1030 if (verbose
) printf ("%s\n", buf
);
1031 gettimeofday (&tv_temp
, NULL
);
1032 my_send (buf
, strlen (buf
));
1033 microsec_headers
= deltime (tv_temp
);
1034 elapsed_time_headers
= (double)microsec_headers
/ 1.0e6
;
1036 /* fetch the page */
1037 full_page
= strdup("");
1038 gettimeofday (&tv_temp
, NULL
);
1039 while ((i
= my_recv (buffer
, MAX_INPUT_BUFFER
-1)) > 0) {
1040 if ((i
>= 1) && (elapsed_time_firstbyte
<= 0.000001)) {
1041 microsec_firstbyte
= deltime (tv_temp
);
1042 elapsed_time_firstbyte
= (double)microsec_firstbyte
/ 1.0e6
;
1045 xasprintf (&full_page_new
, "%s%s", full_page
, buffer
);
1047 full_page
= full_page_new
;
1050 if (no_body
&& document_headers_done (full_page
)) {
1055 microsec_transfer
= deltime (tv_temp
);
1056 elapsed_time_transfer
= (double)microsec_transfer
/ 1.0e6
;
1058 if (i
< 0 && errno
!= ECONNRESET
) {
1062 sslerr=SSL_get_error(ssl, i);
1063 if ( sslerr == SSL_ERROR_SSL ) {
1064 die (STATE_WARNING, _("HTTP WARNING - Client Certificate Required\n"));
1066 die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
1072 die (STATE_CRITICAL
, _("HTTP CRITICAL - Error on receive\n"));
1080 /* return a CRITICAL status if we couldn't read any data */
1081 if (pagesize
== (size_t) 0)
1082 die (STATE_CRITICAL
, _("HTTP CRITICAL - No data received from host\n"));
1084 /* close the connection */
1086 np_net_ssl_cleanup();
1090 /* Save check time */
1091 microsec
= deltime (tv
);
1092 elapsed_time
= (double)microsec
/ 1.0e6
;
1094 /* leave full_page untouched so we can free it later */
1098 printf ("%s://%s:%d%s is %d characters\n",
1099 use_ssl
? "https" : "http", server_address
,
1100 server_port
, server_url
, (int)pagesize
);
1102 /* find status line and null-terminate it */
1104 page
+= (size_t) strcspn (page
, "\r\n");
1106 page
+= (size_t) strspn (page
, "\r\n");
1107 status_line
[strcspn(status_line
, "\r\n")] = 0;
1108 strip (status_line
);
1110 printf ("STATUS: %s\n", status_line
);
1112 /* find header info and null-terminate it */
1114 while (strcspn (page
, "\r\n") > 0) {
1115 page
+= (size_t) strcspn (page
, "\r\n");
1117 if ((strspn (page
, "\r") == 1 && strspn (page
, "\r\n") >= 2) ||
1118 (strspn (page
, "\n") == 1 && strspn (page
, "\r\n") >= 2))
1123 page
+= (size_t) strspn (page
, "\r\n");
1124 header
[pos
- header
] = 0;
1126 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header
,
1127 (no_body
? " [[ skipped ]]" : page
));
1129 /* make sure the status line matches the response we are looking for */
1130 if (!expected_statuscode (status_line
, server_expect
)) {
1131 if (server_port
== HTTP_PORT
)
1133 _("Invalid HTTP response received from host: %s\n"),
1137 _("Invalid HTTP response received from host on port %d: %s\n"),
1138 server_port
, status_line
);
1139 die (STATE_CRITICAL
, "HTTP CRITICAL - %s", msg
);
1142 /* Bypass normal status line check if server_expect was set by user and not default */
1143 /* NOTE: After this if/else block msg *MUST* be an asprintf-allocated string */
1144 if ( server_expect_yn
) {
1146 _("Status line output matched \"%s\" - "), server_expect
);
1148 printf ("%s\n",msg
);
1151 /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
1152 /* HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
1153 /* Status-Code = 3 DIGITS */
1155 status_code
= strchr (status_line
, ' ') + sizeof (char);
1156 if (strspn (status_code
, "1234567890") != 3)
1157 die (STATE_CRITICAL
, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line
);
1159 http_status
= atoi (status_code
);
1161 /* check the return code */
1163 if (http_status
>= 600 || http_status
< 100) {
1164 die (STATE_CRITICAL
, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line
);
1166 /* server errors result in a critical state */
1167 else if (http_status
>= 500) {
1168 xasprintf (&msg
, _("%s - "), status_line
);
1169 result
= STATE_CRITICAL
;
1171 /* client errors result in a warning state */
1172 else if (http_status
>= 400) {
1173 xasprintf (&msg
, _("%s - "), status_line
);
1174 result
= max_state_alt(STATE_WARNING
, result
);
1176 /* check redirected page if specified */
1177 else if (http_status
>= 300) {
1179 if (onredirect
== STATE_DEPENDENT
)
1180 redir (header
, status_line
);
1182 result
= max_state_alt(onredirect
, result
);
1183 xasprintf (&msg
, _("%s - "), status_line
);
1184 } /* end if (http_status >= 300) */
1186 /* Print OK status anyway */
1187 xasprintf (&msg
, _("%s - "), status_line
);
1190 } /* end else (server_expect_yn) */
1192 /* reset the alarm - must be called *after* redir or we'll never die on redirects! */
1195 if (maximum_age
>= 0) {
1196 result
= max_state_alt(check_document_dates(header
, &msg
), result
);
1199 /* Page and Header content checks go here */
1200 if (strlen (header_expect
)) {
1201 if (!strstr (header
, header_expect
)) {
1202 strncpy(&output_header_search
[0],header_expect
,sizeof(output_header_search
));
1203 if(output_header_search
[sizeof(output_header_search
)-1]!='\0') {
1204 bcopy("...",&output_header_search
[sizeof(output_header_search
)-4],4);
1206 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
);
1207 result
= STATE_CRITICAL
;
1212 if (strlen (string_expect
)) {
1213 if (!strstr (page
, string_expect
)) {
1214 strncpy(&output_string_search
[0],string_expect
,sizeof(output_string_search
));
1215 if(output_string_search
[sizeof(output_string_search
)-1]!='\0') {
1216 bcopy("...",&output_string_search
[sizeof(output_string_search
)-4],4);
1218 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
);
1219 result
= STATE_CRITICAL
;
1223 if (strlen (regexp
)) {
1224 errcode
= regexec (&preg
, page
, REGS
, pmatch
, 0);
1225 if ((errcode
== 0 && invert_regex
== 0) || (errcode
== REG_NOMATCH
&& invert_regex
== 1)) {
1226 /* OK - No-op to avoid changing the logic around it */
1227 result
= max_state_alt(STATE_OK
, result
);
1229 else if ((errcode
== REG_NOMATCH
&& invert_regex
== 0) || (errcode
== 0 && invert_regex
== 1)) {
1230 if (invert_regex
== 0)
1231 xasprintf (&msg
, _("%spattern not found, "), msg
);
1233 xasprintf (&msg
, _("%spattern found, "), msg
);
1234 result
= STATE_CRITICAL
;
1237 /* FIXME: Shouldn't that be UNKNOWN? */
1238 regerror (errcode
, &preg
, errbuf
, MAX_INPUT_BUFFER
);
1239 xasprintf (&msg
, _("%sExecute Error: %s, "), msg
, errbuf
);
1240 result
= STATE_CRITICAL
;
1244 /* make sure the page is of an appropriate size */
1245 /* page_len = get_content_length(header); */
1246 /* FIXME: Will this work with -N ? IMHO we should use
1247 * get_content_length(header) and always check if it's different than the
1250 /* FIXME: IIRC pagesize returns headers - shouldn't we make
1251 * it == get_content_length(header) ??
1253 page_len
= pagesize
;
1254 if ((max_page_len
> 0) && (page_len
> max_page_len
)) {
1255 xasprintf (&msg
, _("%spage size %d too large, "), msg
, page_len
);
1256 result
= max_state_alt(STATE_WARNING
, result
);
1257 } else if ((min_page_len
> 0) && (page_len
< min_page_len
)) {
1258 xasprintf (&msg
, _("%spage size %d too small, "), msg
, page_len
);
1259 result
= max_state_alt(STATE_WARNING
, result
);
1262 /* Cut-off trailing characters */
1263 if(msg
[strlen(msg
)-2] == ',')
1264 msg
[strlen(msg
)-2] = '\0';
1266 msg
[strlen(msg
)-3] = '\0';
1268 /* check elapsed time */
1269 if (show_extended_perfdata
)
1271 _("%s - %d bytes in %.3f second response time %s|%s %s %s %s %s %s %s"),
1272 msg
, page_len
, elapsed_time
,
1273 (display_html
? "</A>" : ""),
1274 perfd_time (elapsed_time
),
1275 perfd_size (page_len
),
1276 perfd_time_connect (elapsed_time_connect
),
1277 use_ssl
== TRUE
? perfd_time_ssl (elapsed_time_ssl
) : "",
1278 perfd_time_headers (elapsed_time_headers
),
1279 perfd_time_firstbyte (elapsed_time_firstbyte
),
1280 perfd_time_transfer (elapsed_time_transfer
));
1283 _("%s - %d bytes in %.3f second response time %s|%s %s"),
1284 msg
, page_len
, elapsed_time
,
1285 (display_html
? "</A>" : ""),
1286 perfd_time (elapsed_time
),
1287 perfd_size (page_len
));
1289 result
= max_state_alt(get_status(elapsed_time
, thlds
), result
);
1291 die (result
, "HTTP %s: %s\n", state_text(result
), msg
);
1293 return STATE_UNKNOWN
;
1299 #define URI_HTTP "%5[HTPShtps]"
1300 #define URI_HOST "%255[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1301 #define URI_PORT "%6d" /* MAX_PORT's width is 5 chars, 6 to detect overflow */
1302 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1303 #define HD1 URI_HTTP "://" URI_HOST ":" URI_PORT "/" URI_PATH
1304 #define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH
1305 #define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT
1306 #define HD4 URI_HTTP "://" URI_HOST
1307 #define HD5 URI_PATH
1310 redir (char *pos
, char *status_line
)
1319 addr
= malloc (MAX_IPV4_HOSTLENGTH
+ 1);
1321 die (STATE_UNKNOWN
, _("HTTP UNKNOWN - Could not allocate addr\n"));
1323 memset(addr
, 0, MAX_IPV4_HOSTLENGTH
);
1324 url
= malloc (strcspn (pos
, "\r\n"));
1326 die (STATE_UNKNOWN
, _("HTTP UNKNOWN - Could not allocate URL\n"));
1329 sscanf (pos
, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx
, &i
);
1331 pos
+= (size_t) strcspn (pos
, "\r\n");
1332 pos
+= (size_t) strspn (pos
, "\r\n");
1333 if (strlen(pos
) == 0)
1335 _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"),
1336 status_line
, (display_html
? "</A>" : ""));
1341 pos
+= strspn (pos
, " \t");
1344 * RFC 2616 (4.2): ``Header fields can be extended over multiple lines by
1345 * preceding each extra line with at least one SP or HT.''
1347 for (; (i
= strspn (pos
, "\r\n")); pos
+= i
) {
1349 if (!(i
= strspn (pos
, " \t"))) {
1350 die (STATE_UNKNOWN
, _("HTTP UNKNOWN - Empty redirect location%s\n"),
1351 display_html
? "</A>" : "");
1355 url
= realloc (url
, strcspn (pos
, "\r\n") + 1);
1357 die (STATE_UNKNOWN
, _("HTTP UNKNOWN - Could not allocate URL\n"));
1359 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
1360 if (sscanf (pos
, HD1
, type
, addr
, &i
, url
) == 4) {
1361 url
= prepend_slash (url
);
1362 use_ssl
= server_type_check (type
);
1365 /* URI_HTTP URI_HOST URI_PATH */
1366 else if (sscanf (pos
, HD2
, type
, addr
, url
) == 3 ) {
1367 url
= prepend_slash (url
);
1368 use_ssl
= server_type_check (type
);
1369 i
= server_port_check (use_ssl
);
1372 /* URI_HTTP URI_HOST URI_PORT */
1373 else if (sscanf (pos
, HD3
, type
, addr
, &i
) == 3) {
1374 strcpy (url
, HTTP_URL
);
1375 use_ssl
= server_type_check (type
);
1378 /* URI_HTTP URI_HOST */
1379 else if (sscanf (pos
, HD4
, type
, addr
) == 2) {
1380 strcpy (url
, HTTP_URL
);
1381 use_ssl
= server_type_check (type
);
1382 i
= server_port_check (use_ssl
);
1386 else if (sscanf (pos
, HD5
, url
) == 1) {
1388 if ((url
[0] != '/')) {
1389 if ((x
= strrchr(server_url
, '/')))
1391 xasprintf (&url
, "%s/%s", server_url
, url
);
1394 strcpy (type
, server_type
);
1395 strcpy (addr
, host_name
? host_name
: server_address
);
1400 _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"),
1401 pos
, (display_html
? "</A>" : ""));
1406 } /* end while (pos) */
1408 if (++redir_depth
> max_depth
)
1410 _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
1411 max_depth
, type
, addr
, i
, url
, (display_html
? "</A>" : ""));
1413 if (server_port
==i
&&
1414 !strncmp(server_address
, addr
, MAX_IPV4_HOSTLENGTH
) &&
1415 (host_name
&& !strncmp(host_name
, addr
, MAX_IPV4_HOSTLENGTH
)) &&
1416 !strcmp(server_url
, url
))
1418 _("HTTP WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1419 type
, addr
, i
, url
, (display_html
? "</A>" : ""));
1421 strcpy (server_type
, type
);
1424 host_name
= strndup (addr
, MAX_IPV4_HOSTLENGTH
);
1426 if (!(followsticky
& STICKY_HOST
)) {
1427 free (server_address
);
1428 server_address
= strndup (addr
, MAX_IPV4_HOSTLENGTH
);
1430 if (!(followsticky
& STICKY_PORT
)) {
1437 if (server_port
> MAX_PORT
)
1439 _("HTTP UNKNOWN - Redirection to port above %d - %s://%s:%d%s%s\n"),
1440 MAX_PORT
, server_type
, server_address
, server_port
, server_url
,
1441 display_html
? "</A>" : "");
1443 /* reset virtual port */
1444 virtual_port
= server_port
;
1447 printf (_("Redirection to %s://%s:%d%s\n"), server_type
,
1448 host_name
? host_name
: server_address
, server_port
, server_url
);
1456 server_type_check (const char *type
)
1458 if (strcmp (type
, "https"))
1465 server_port_check (int ssl_flag
)
1473 char *perfd_time (double elapsed_time
)
1475 return fperfdata ("time", elapsed_time
, "s",
1476 thlds
->warning
?TRUE
:FALSE
, thlds
->warning
?thlds
->warning
->end
:0,
1477 thlds
->critical
?TRUE
:FALSE
, thlds
->critical
?thlds
->critical
->end
:0,
1481 char *perfd_time_connect (double elapsed_time_connect
)
1483 return fperfdata ("time_connect", elapsed_time_connect
, "s", FALSE
, 0, FALSE
, 0, FALSE
, 0, FALSE
, 0);
1486 char *perfd_time_ssl (double elapsed_time_ssl
)
1488 return fperfdata ("time_ssl", elapsed_time_ssl
, "s", FALSE
, 0, FALSE
, 0, FALSE
, 0, FALSE
, 0);
1491 char *perfd_time_headers (double elapsed_time_headers
)
1493 return fperfdata ("time_headers", elapsed_time_headers
, "s", FALSE
, 0, FALSE
, 0, FALSE
, 0, FALSE
, 0);
1496 char *perfd_time_firstbyte (double elapsed_time_firstbyte
)
1498 return fperfdata ("time_firstbyte", elapsed_time_firstbyte
, "s", FALSE
, 0, FALSE
, 0, FALSE
, 0, FALSE
, 0);
1501 char *perfd_time_transfer (double elapsed_time_transfer
)
1503 return fperfdata ("time_transfer", elapsed_time_transfer
, "s", FALSE
, 0, FALSE
, 0, FALSE
, 0, FALSE
, 0);
1506 char *perfd_size (int page_len
)
1508 return perfdata ("size", page_len
, "B",
1509 (min_page_len
>0?TRUE
:FALSE
), min_page_len
,
1510 (min_page_len
>0?TRUE
:FALSE
), 0,
1517 print_revision (progname
, NP_VERSION
);
1519 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1520 printf (COPYRIGHT
, copyright
, email
);
1522 printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
1523 printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
1524 printf ("%s\n", _("strings and regular expressions, check connection times, and report on"));
1525 printf ("%s\n", _("certificate expiration times."));
1531 printf (_("NOTE: One or both of -H and -I must be specified"));
1535 printf (UT_HELP_VRSN
);
1536 printf (UT_EXTRA_OPTS
);
1538 printf (" %s\n", "-H, --hostname=ADDRESS");
1539 printf (" %s\n", _("Host name argument for servers using host headers (virtual host)"));
1540 printf (" %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1541 printf (" %s\n", "-I, --IP-address=ADDRESS");
1542 printf (" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1543 printf (" %s\n", "-p, --port=INTEGER");
1544 printf (" %s", _("Port number (default: "));
1545 printf ("%d)\n", HTTP_PORT
);
1550 printf (" %s\n", "-S, --ssl=VERSION[+]");
1551 printf (" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents"));
1552 printf (" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,"));
1553 printf (" %s\n", _("1.2 = TLSv1.2). With a '+' suffix, newer versions are also accepted."));
1554 printf (" %s\n", "--sni");
1555 printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
1556 printf (" %s\n", "-C, --certificate=INTEGER[,INTEGER]");
1557 printf (" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
1558 printf (" %s\n", _("(when this option is used the URL is not checked.)"));
1559 printf (" %s\n", "-J, --client-cert=FILE");
1560 printf (" %s\n", _("Name of file that contains the client certificate (PEM format)"));
1561 printf (" %s\n", _("to be used in establishing the SSL session"));
1562 printf (" %s\n", "-K, --private-key=FILE");
1563 printf (" %s\n", _("Name of file containing the private key (PEM format)"));
1564 printf (" %s\n", _("matching the client certificate"));
1567 printf (" %s\n", "-e, --expect=STRING");
1568 printf (" %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1569 printf (" %s", _("the first (status) line of the server response (default: "));
1570 printf ("%s)\n", HTTP_EXPECT
);
1571 printf (" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1572 printf (" %s\n", "-d, --header-string=STRING");
1573 printf (" %s\n", _("String to expect in the response headers"));
1574 printf (" %s\n", "-s, --string=STRING");
1575 printf (" %s\n", _("String to expect in the content"));
1576 printf (" %s\n", "-u, --url=PATH");
1577 printf (" %s\n", _("URL to GET or POST (default: /)"));
1578 printf (" %s\n", "-P, --post=STRING");
1579 printf (" %s\n", _("URL encoded http POST data"));
1580 printf (" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT)");
1581 printf (" %s\n", _("Set HTTP method."));
1582 printf (" %s\n", "-N, --no-body");
1583 printf (" %s\n", _("Don't wait for document body: stop reading after headers."));
1584 printf (" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
1585 printf (" %s\n", "-M, --max-age=SECONDS");
1586 printf (" %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
1587 printf (" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1588 printf (" %s\n", "-T, --content-type=STRING");
1589 printf (" %s\n", _("specify Content-Type header media type when POSTing\n"));
1591 printf (" %s\n", "-l, --linespan");
1592 printf (" %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1593 printf (" %s\n", "-r, --regex, --ereg=STRING");
1594 printf (" %s\n", _("Search page for regex STRING"));
1595 printf (" %s\n", "-R, --eregi=STRING");
1596 printf (" %s\n", _("Search page for case-insensitive regex STRING"));
1597 printf (" %s\n", "--invert-regex");
1598 printf (" %s\n", _("Return CRITICAL if found, OK if not\n"));
1600 printf (" %s\n", "-a, --authorization=AUTH_PAIR");
1601 printf (" %s\n", _("Username:password on sites with basic authentication"));
1602 printf (" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
1603 printf (" %s\n", _("Username:password on proxy-servers with basic authentication"));
1604 printf (" %s\n", "-A, --useragent=STRING");
1605 printf (" %s\n", _("String to be sent in http header as \"User Agent\""));
1606 printf (" %s\n", "-k, --header=STRING");
1607 printf (" %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers"));
1608 printf (" %s\n", "-E, --extended-perfdata");
1609 printf (" %s\n", _("Print additional performance data"));
1610 printf (" %s\n", "-L, --link");
1611 printf (" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1612 printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>");
1613 printf (" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1614 printf (" %s\n", _("specified IP address. stickyport also ensures port stays the same."));
1615 printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1616 printf (" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1618 printf (UT_WARN_CRIT
);
1620 printf (UT_CONN_TIMEOUT
, DEFAULT_SOCKET_TIMEOUT
);
1622 printf (UT_VERBOSE
);
1625 printf ("%s\n", _("Notes:"));
1626 printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1627 printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1628 printf (" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect reponse"));
1629 printf (" %s\n", _("messages from the host result in STATE_WARNING return values. If you are"));
1630 printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1631 printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
1635 printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
1636 printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
1637 printf (" %s\n", _("certificate is still valid for the specified number of days."));
1639 printf (" %s\n", _("Please note that this plugin does not check if the presented server"));
1640 printf (" %s\n", _("certificate matches the hostname of the server, or if the certificate"));
1641 printf (" %s\n", _("has a valid chain of trust to one of the locally installed CAs."));
1643 printf ("%s\n", _("Examples:"));
1644 printf (" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com");
1645 printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
1646 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1647 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1648 printf (" %s\n", _("a STATE_CRITICAL will be returned."));
1650 printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 14");
1651 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1652 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1653 printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
1654 printf (" %s\n\n", _("the certificate is expired."));
1656 printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 30,14");
1657 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 30 days,"));
1658 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1659 printf (" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned."));
1660 printf (" %s\n", _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days"));
1662 printf (" %s\n\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: ");
1663 printf (" %s\n", _("check_http -I 192.168.100.35 -p 80 -u https://www.verisign.com/ -S -j CONNECT -H www.verisign.com "));
1664 printf (" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> -S(sl) -j CONNECT -H <webserver>"));
1665 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1666 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1667 printf (" %s\n", _("a STATE_CRITICAL will be returned."));
1671 printf (UT_SUPPORT
);
1680 printf ("%s\n", _("Usage:"));
1681 printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname
);
1682 printf (" [-J <client certificate file>] [-K <private key>]\n");
1683 printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n");
1684 printf (" [-b proxy_auth] [-f <ok|warning|critcal|follow|sticky|stickyport>]\n");
1685 printf (" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
1686 printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
1687 printf (" [-A string] [-k string] [-S <version>] [--sni] [-C <warn_age>[,<crit_age>]]\n");
1688 printf (" [-T <content-type>] [-j method]\n");