1 /*****************************************************************************
2 *
3 * Nagios check_http plugin
4 *
5 * License: GPL
6 * Copyright (c) 1999-2014 Nagios Plugins Development Team
7 *
8 * Description:
9 *
10 * This file contains the check_http plugin
11 *
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.
16 *
17 *
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.
22 *
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.
27 *
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/>.
30 *
31 *
32 *****************************************************************************/
33 
34 /* splint -I. -I../../plugins -I../../lib/ -I/usr/kerberos/include/ ../../plugins/check_http.c */
35 
36 const char *progname = "check_http";
37 const char *copyright = "1999-2014";
38 const char *email = "devel@nagios-plugins.org";
39 
40 #include "common.h"
41 #include "netutils.h"
42 #include "utils.h"
43 #include "base64.h"
44 #include <ctype.h>
45 
46 #define STICKY_NONE 0
47 #define STICKY_HOST 1
48 #define STICKY_PORT 2
49 
50 #define HTTP_EXPECT "HTTP/1."
51 enum {
52     MAX_IPV4_HOSTLENGTH = 255,
53     HTTP_PORT = 80,
54     HTTPS_PORT = 443,
55     MAX_PORT = 65535
56 };
57 
58 #ifdef HAVE_SSL
59 int check_cert = FALSE;
60 int continue_after_check_cert = FALSE;
61 int ssl_version = 0;
62 int days_till_exp_warn, days_till_exp_crit;
63 char *randbuff;
64 X509 *server_cert;
65 #  define my_recv(buf, len) ((use_ssl) ? np_net_ssl_read(buf, len) : read(sd, buf, len))
66 #  define my_send(buf, len) ((use_ssl) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0))
67 #else /* ifndef HAVE_SSL */
68 #  define my_recv(buf, len) read(sd, buf, len)
69 #  define my_send(buf, len) send(sd, buf, len, 0)
70 #endif /* HAVE_SSL */
71 int no_body = FALSE;
72 int maximum_age = -1;
73 
74 enum {
75     REGS = 2,
76     MAX_RE_SIZE = 2048
77 };
78 #include "regex.h"
79 regex_t preg;
80 regmatch_t pmatch[REGS];
81 char regexp[MAX_RE_SIZE];
82 char errbuf[MAX_INPUT_BUFFER];
83 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
84 int errcode;
85 int invert_regex = 0;
86 
87 struct timeval tv;
88 struct timeval tv_temp;
89 
90 #define HTTP_URL "/"
91 #define CRLF "\r\n"
92 
93 int specify_port = FALSE;
94 int server_port = HTTP_PORT;
95 char server_port_text[6] = "";
96 char server_type[6] = "http";
97 char *server_address;
98 char *host_name;
99 char *server_url;
100 char *user_agent;
101 int server_url_length;
102 int server_expect_yn = 0;
103 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
104 char header_expect[MAX_INPUT_BUFFER] = "";
105 char string_expect[MAX_INPUT_BUFFER] = "";
106 char output_header_search[30] = "";
107 char output_string_search[30] = "";
108 char *warning_thresholds = NULL;
109 char *critical_thresholds = NULL;
110 thresholds *thlds;
111 char user_auth[MAX_INPUT_BUFFER] = "";
112 char proxy_auth[MAX_INPUT_BUFFER] = "";
113 int display_html = FALSE;
114 char **http_opt_headers = NULL;
115 int http_opt_headers_count = 0;
116 int have_accept = FALSE;
117 int onredirect = STATE_OK;
118 int followsticky = STICKY_NONE;
119 int use_ssl = FALSE;
120 int use_sni = FALSE;
121 int verbose = FALSE;
122 int show_extended_perfdata = FALSE;
123 int show_url = FALSE;
124 int sd;
125 int min_page_len = 0;
126 int max_page_len = 0;
127 int redir_depth = 0;
128 int max_depth = 15;
129 char *http_method;
130 char *http_post_data;
131 char *http_content_type;
132 char buffer[MAX_INPUT_BUFFER];
133 char *client_cert = NULL;
134 char *client_privkey = NULL;
135 
136 int process_arguments (int, char **);
137 int check_http (void);
138 void redir (char *pos, char *status_line);
139 int server_type_check(const char *type);
140 int server_port_check(int ssl_flag);
141 char *perfd_time (double microsec);
142 char *perfd_time_connect (double microsec);
143 char *perfd_time_ssl (double microsec);
144 char *perfd_time_firstbyte (double microsec);
145 char *perfd_time_headers (double microsec);
146 char *perfd_time_transfer (double microsec);
147 char *perfd_size (int page_len);
148 void print_help (void);
149 void print_usage (void);
150 
151 extern int check_hostname;
152 
153 
154 int
main(int argc,char ** argv)155 main (int argc, char **argv)
156 {
157     int result = STATE_UNKNOWN;
158 
159     setlocale (LC_ALL, ""); setlocale(LC_NUMERIC, "C");
160     bindtextdomain (PACKAGE, LOCALEDIR);
161     textdomain (PACKAGE);
162 
163     /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
164     server_url = strdup(HTTP_URL);
165     server_url_length = strlen(server_url);
166     xasprintf (&user_agent, "User-Agent: check_http/v%s (nagios-plugins %s)",
167                NP_VERSION, VERSION);
168 
169     /* Parse extra opts if any */
170     argv=np_extra_opts (&argc, argv, progname);
171 
172     if (process_arguments (argc, argv) == ERROR)
173         usage4 (_("Could not parse arguments"));
174 
175     if (display_html == TRUE)
176         printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">",
177                 use_ssl ? "https" : "http", host_name ? host_name : server_address,
178                 server_port, server_url);
179 
180     /* initialize alarm signal handling, set socket timeout, start timer */
181     (void) signal (SIGALRM, socket_timeout_alarm_handler);
182     (void) alarm (timeout_interval);
183     gettimeofday (&tv, NULL);
184 
185     result = check_http ();
186     return result;
187 }
188 
189 /* Plugin-specific wrapper for vdie() */
190 void
check_http_die(int state,char * fmt,...)191 check_http_die(int state, char* fmt, ...)
192 {
193     char* msg = malloc(4096);
194     va_list ap;
195     va_start(ap, fmt);
196     vsnprintf(msg, 4096, fmt, ap );
197     va_end(ap);
198 
199     if (show_url) {
200         die (state, "HTTP %s - %s://%s:%d%s - %s", state_text(state), use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url, msg);
201     }
202     else {
203         die (state, "HTTP %s - %s", state_text(state), msg);
204     }
205 }
206 
207 /* check whether a file exists */
208 void
test_file(char * path)209 test_file (char *path)
210 {
211     if (access(path, R_OK) == 0)
212         return;
213     usage2 (_("file does not exist or is not readable"), path);
214 }
215 
216 /* process command-line arguments */
217 int
process_arguments(int argc,char ** argv)218 process_arguments (int argc, char **argv)
219 {
220     int c = 1;
221     char *p;
222     char *temp;
223 
224     enum {
225         INVERT_REGEX = CHAR_MAX + 1,
226         SNI_OPTION,
227         VERIFY_HOST,
228         CONTINUE_AFTER_CHECK_CERT
229     };
230 
231     int option = 0;
232     static struct option longopts[] = {
233         STD_LONG_OPTS,
234         {"link", no_argument, 0, 'L'},
235         {"nohtml", no_argument, 0, 'n'},
236         {"ssl", optional_argument, 0, 'S'},
237         {"sni", no_argument, 0, SNI_OPTION},
238         {"verify-host", no_argument, 0, VERIFY_HOST},
239         {"post", required_argument, 0, 'P'},
240         {"method", required_argument, 0, 'j'},
241         {"IP-address", required_argument, 0, 'I'},
242         {"url", required_argument, 0, 'u'},
243         {"uri", required_argument, 0, 'u'},
244         {"port", required_argument, 0, 'p'},
245         {"authorization", required_argument, 0, 'a'},
246         {"proxy-authorization", required_argument, 0, 'b'},
247         {"header-string", required_argument, 0, 'd'},
248         {"string", required_argument, 0, 's'},
249         {"expect", required_argument, 0, 'e'},
250         {"regex", required_argument, 0, 'r'},
251         {"ereg", required_argument, 0, 'r'},
252         {"eregi", required_argument, 0, 'R'},
253         {"linespan", no_argument, 0, 'l'},
254         {"onredirect", required_argument, 0, 'f'},
255         {"certificate", required_argument, 0, 'C'},
256         {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT},
257         {"client-cert", required_argument, 0, 'J'},
258         {"private-key", required_argument, 0, 'K'},
259         {"useragent", required_argument, 0, 'A'},
260         {"header", required_argument, 0, 'k'},
261         {"no-body", no_argument, 0, 'N'},
262         {"max-age", required_argument, 0, 'M'},
263         {"content-type", required_argument, 0, 'T'},
264         {"pagesize", required_argument, 0, 'm'},
265         {"invert-regex", no_argument, NULL, INVERT_REGEX},
266         {"use-ipv4", no_argument, 0, '4'},
267         {"use-ipv6", no_argument, 0, '6'},
268         {"extended-perfdata", no_argument, 0, 'E'},
269         {"show-url", no_argument, 0, 'U'},
270         {0, 0, 0, 0}
271     };
272 
273     if (argc < 2)
274         return ERROR;
275 
276     for (c = 1; c < argc; c++) {
277         if (strcmp ("-to", argv[c]) == 0)
278             strcpy (argv[c], "-t");
279         if (strcmp ("-hn", argv[c]) == 0)
280             strcpy (argv[c], "-H");
281         if (strcmp ("-wt", argv[c]) == 0)
282             strcpy (argv[c], "-w");
283         if (strcmp ("-ct", argv[c]) == 0)
284             strcpy (argv[c], "-c");
285         if (strcmp ("-nohtml", argv[c]) == 0)
286             strcpy (argv[c], "-n");
287     }
288 
289     while (1) {
290         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:NEU", longopts, &option);
291         if (c == -1 || c == EOF)
292             break;
293 
294         switch (c) {
295         case '?': /* usage */
296             usage5 ();
297             break;
298         case 'h': /* help */
299             print_help ();
300             exit (STATE_OK);
301             break;
302         case 'V': /* version */
303             print_revision (progname, NP_VERSION);
304             exit (STATE_OK);
305             break;
306         case 't': /* timeout period */
307             timeout_interval = parse_timeout_string(optarg);
308             break;
309         case 'c': /* critical time threshold */
310             critical_thresholds = optarg;
311             break;
312         case 'w': /* warning time threshold */
313             warning_thresholds = optarg;
314             break;
315         case 'A': /* User Agent String */
316             xasprintf (&user_agent, "User-Agent: %s", optarg);
317             break;
318         case 'k': /* Additional headers */
319             /*      if (http_opt_headers_count == 0)
320                     http_opt_headers = malloc (sizeof (char *) * (++http_opt_headers_count));
321                   else */
322             http_opt_headers = realloc (http_opt_headers, sizeof(char *) * (++http_opt_headers_count));
323             http_opt_headers[http_opt_headers_count - 1] = optarg;
324             if (!strncmp(optarg, "Accept:", 7))
325                 have_accept = TRUE;
326             break;
327         case 'L': /* show html link */
328             display_html = TRUE;
329             break;
330         case 'n': /* do not show html link */
331             display_html = FALSE;
332             break;
333         case 'C': /* Check SSL cert validity */
334 #ifdef HAVE_SSL
335             if ((temp=strchr(optarg,','))!=NULL) {
336                 *temp='\0';
337                 if (!is_intnonneg (optarg))
338                     usage2 (_("Invalid certificate expiration period"), optarg);
339                 days_till_exp_warn = atoi(optarg);
340                 *temp=',';
341                 temp++;
342                 if (!is_intnonneg (temp))
343                     usage2 (_("Invalid certificate expiration period"), temp);
344                 days_till_exp_crit = atoi (temp);
345             } else {
346                 days_till_exp_crit=0;
347                 if (!is_intnonneg (optarg))
348                     usage2 (_("Invalid certificate expiration period"), optarg);
349                 days_till_exp_warn = atoi (optarg);
350             }
351             check_cert = TRUE;
352             goto enable_ssl;
353 #endif
354         case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */
355 #ifdef HAVE_SSL
356             continue_after_check_cert = TRUE;
357             break;
358 #endif
359         case 'J': /* use client certificate */
360 #ifdef HAVE_SSL
361             test_file(optarg);
362             client_cert = optarg;
363             goto enable_ssl;
364 #endif
365         case 'K': /* use client private key */
366 #ifdef HAVE_SSL
367             test_file(optarg);
368             client_privkey = optarg;
369             goto enable_ssl;
370 #endif
371         case 'S': /* use SSL */
372 #ifdef HAVE_SSL
373 enable_ssl:
374             /* ssl_version initialized to 0 as a default. Only set if it's non-zero.  This helps when we include multiple
375                parameters, like -S and -C combinations */
376             use_ssl = TRUE;
377             if (c=='S' && optarg != NULL) {
378                 int got_plus = strchr(optarg, '+') != NULL;
379 
380                 if (!strncmp (optarg, "1.3", 3))
381                     ssl_version = got_plus ? MP_TLSv1_3_OR_NEWER : MP_TLSv1_3;
382                 else if (!strncmp (optarg, "1.2", 3))
383                     ssl_version = got_plus ? MP_TLSv1_2_OR_NEWER : MP_TLSv1_2;
384                 else if (!strncmp (optarg, "1.1", 3))
385                     ssl_version = got_plus ? MP_TLSv1_1_OR_NEWER : MP_TLSv1_1;
386                 else if (optarg[0] == '1')
387                     ssl_version = got_plus ? MP_TLSv1_OR_NEWER : MP_TLSv1;
388                 else if (optarg[0] == '3')
389                     ssl_version = got_plus ? MP_SSLv3_OR_NEWER : MP_SSLv3;
390                 else if (optarg[0] == '2')
391                     ssl_version = got_plus ? MP_SSLv2_OR_NEWER : MP_SSLv2;
392                 else
393                     usage4 (_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 (with optional '+' suffix)"));
394             }
395             if (specify_port == FALSE)
396                 server_port = HTTPS_PORT;
397 #else
398             /* -C -J and -K fall through to here without SSL */
399             usage4 (_("Invalid option - SSL is not available"));
400 #endif
401             break;
402         case SNI_OPTION:
403             use_sni = TRUE;
404             break;
405         case VERIFY_HOST:
406             check_hostname = 1;
407             break;
408         case 'f': /* onredirect */
409             if (!strcmp (optarg, "stickyport"))
410                 onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST|STICKY_PORT;
411             else if (!strcmp (optarg, "sticky"))
412                 onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST;
413             else if (!strcmp (optarg, "follow"))
414                 onredirect = STATE_DEPENDENT, followsticky = STICKY_NONE;
415             else if (!strcmp (optarg, "unknown"))
416                 onredirect = STATE_UNKNOWN;
417             else if (!strcmp (optarg, "ok"))
418                 onredirect = STATE_OK;
419             else if (!strcmp (optarg, "warning"))
420                 onredirect = STATE_WARNING;
421             else if (!strcmp (optarg, "critical"))
422                 onredirect = STATE_CRITICAL;
423             else usage2 (_("Invalid onredirect option"), optarg);
424             if (verbose)
425                 printf(_("option f:%d \n"), onredirect);
426             break;
427             /* Note: H, I, and u must be malloc'd or will fail on redirects */
428         case 'H': /* Host Name (virtual host) */
429             host_name = strdup(optarg);
430             if (*host_name == '[') {
431                 if ((p = strstr (host_name, "]:")) != NULL) /* [IPv6]:port */ {
432                     server_port = atoi (p + 2);
433                     *++p = '\0'; // Set The host_name sans ":port"
434                 }
435             } else if ((p = strchr (host_name, ':')) != NULL
436                        && strchr (++p, ':') == NULL) /* IPv4:port or host:port */ {
437                 server_port = atoi (p);
438                 *--p = '\0'; // Set The host_name sans ":port"
439             }
440             break;
441         case 'I': /* Server IP-address */
442             server_address = strdup (optarg);
443             break;
444         case 'u': /* URL path */
445             /* server_url is first allocated in main() */
446             free(server_url);
447             server_url = strdup (optarg);
448             server_url_length = strlen (server_url);
449             break;
450         case 'p': /* Server port */
451             if (!is_intnonneg (optarg))
452                 usage2 (_("Invalid port number"), optarg);
453             else {
454                 server_port = atoi (optarg);
455                 specify_port = TRUE;
456             }
457             break;
458         case 'a': /* authorization info */
459             strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
460             user_auth[MAX_INPUT_BUFFER - 1] = 0;
461             break;
462         case 'b': /* proxy-authorization info */
463             strncpy (proxy_auth, optarg, MAX_INPUT_BUFFER - 1);
464             proxy_auth[MAX_INPUT_BUFFER - 1] = 0;
465             break;
466         case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
467             if (! http_post_data)
468                 http_post_data = strdup (optarg);
469             if (! http_method)
470                 http_method = strdup("POST");
471             break;
472         case 'j': /* Set HTTP method */
473             if (http_method)
474                 free(http_method);
475             http_method = strdup (optarg);
476             break;
477         case 'd': /* string or substring */
478             strncpy (header_expect, optarg, MAX_INPUT_BUFFER - 1);
479             header_expect[MAX_INPUT_BUFFER - 1] = 0;
480             break;
481         case 's': /* string or substring */
482             strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
483             string_expect[MAX_INPUT_BUFFER - 1] = 0;
484             break;
485         case 'e': /* string or substring */
486             strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
487             server_expect[MAX_INPUT_BUFFER - 1] = 0;
488             server_expect_yn = 1;
489             break;
490         case 'T': /* Content-type */
491             xasprintf (&http_content_type, "%s", optarg);
492             break;
493         case 'l': /* linespan */
494             cflags &= ~REG_NEWLINE;
495             break;
496         case 'R': /* regex */
497             cflags |= REG_ICASE;
498         case 'r': /* regex */
499             strncpy (regexp, optarg, MAX_RE_SIZE - 1);
500             regexp[MAX_RE_SIZE - 1] = 0;
501             errcode = regcomp (&preg, regexp, cflags);
502             if (errcode != 0) {
503                 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
504                 printf (_("Could Not Compile Regular Expression: %s"), errbuf);
505                 return ERROR;
506             }
507             break;
508         case INVERT_REGEX:
509             invert_regex = 1;
510             break;
511         case '4':
512             address_family = AF_INET;
513             break;
514         case '6':
515 
516 #ifdef USE_IPV6
517             address_family = AF_INET6;
518 #else
519             usage4 (_("IPv6 support not available"));
520 #endif
521 
522             break;
523         case 'v': /* verbose */
524             verbose = TRUE;
525             break;
526         case 'm': { /* min_page_length */
527             char *tmp;
528             if (strchr(optarg, ':') != (char *)NULL) {
529                 /* range, so get two values, min:max */
530                 tmp = strtok(optarg, ":");
531                 if (tmp == NULL) {
532                     printf("Bad format: try \"-m min:max\"\n");
533                     exit (STATE_WARNING);
534                 } else
535                     min_page_len = atoi(tmp);
536 
537                 tmp = strtok(NULL, ":");
538                 if (tmp == NULL) {
539                     printf("Bad format: try \"-m min:max\"\n");
540                     exit (STATE_WARNING);
541                 } else
542                     max_page_len = atoi(tmp);
543             } else
544                 min_page_len = atoi (optarg);
545             break;
546         }
547         case 'N': /* no-body */
548             no_body = TRUE;
549             break;
550         case 'M': { /* max-age */
551             int L = strlen(optarg);
552             if (L && optarg[L-1] == 'm')
553                 maximum_age = atoi (optarg) * 60;
554             else if (L && optarg[L-1] == 'h')
555                 maximum_age = atoi (optarg) * 60 * 60;
556             else if (L && optarg[L-1] == 'd')
557                 maximum_age = atoi (optarg) * 60 * 60 * 24;
558             else if (L && (optarg[L-1] == 's' ||
559                            isdigit (optarg[L-1])))
560                 maximum_age = atoi (optarg);
561             else {
562                 fprintf (stderr, "unparsable max-age: %s\n", optarg);
563                 exit (STATE_WARNING);
564             }
565         }
566         break;
567         case 'E': /* show extended perfdata */
568             show_extended_perfdata = TRUE;
569             break;
570         case 'U': /* show checked url in output msg */
571           show_url = TRUE;
572           break;
573         }
574     }
575 
576     c = optind;
577 
578     if (server_address == NULL && c < argc)
579         server_address = strdup (argv[c++]);
580 
581     if (host_name == NULL && c < argc)
582         host_name = strdup (argv[c++]);
583 
584     if (use_sni && host_name == NULL) {
585         usage4(_("Server name indication requires that a host name is defined with -H"));
586     }
587 
588     if (server_address == NULL) {
589         if (host_name == NULL)
590             usage4 (_("You must specify a server address or host name"));
591         else
592             server_address = strdup (host_name);
593     }
594 
595     set_thresholds(&thlds, warning_thresholds, critical_thresholds);
596 
597     if (critical_thresholds && thlds->critical->end>(double)timeout_interval)
598         timeout_interval = (int)thlds->critical->end + 1;
599 
600     if (http_method == NULL)
601         http_method = strdup ("GET");
602 
603     if (client_cert && !client_privkey)
604         usage4 (_("If you use a client certificate you must also specify a private key file"));
605 
606     return TRUE;
607 }
608 
609 
610 
611 /* Returns 0 if we're still retrieving the headers.
612  * Otherwise, returns the length of the header (not including the final newlines)
613  */
614 static int
document_headers_done(const char * full_page)615 document_headers_done (const char *full_page)
616 {
617     const char *body;
618 
619     for (body = full_page; *body; body++) {
620         if (!strncmp (body, "\n\n", 2) || !strncmp (body, "\n\r\n", 3))
621             break;
622     }
623 
624     if (!*body)
625         return 0;  /* haven't read end of headers yet */
626 
627     return body - full_page;
628 }
629 
630 static time_t
parse_time_string(const char * string)631 parse_time_string (const char *string)
632 {
633     struct tm tm;
634     time_t t;
635     memset (&tm, 0, sizeof(tm));
636 
637     /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */
638 
639     if (isupper (string[0])  &&  /* Tue */
640             islower (string[1])  &&
641             islower (string[2])  &&
642             ',' ==   string[3]   &&
643             ' ' ==   string[4]   &&
644             (isdigit(string[5]) || string[5] == ' ') &&   /* 25 */
645             isdigit (string[6])  &&
646             ' ' ==   string[7]   &&
647             isupper (string[8])  &&  /* Dec */
648             islower (string[9])  &&
649             islower (string[10]) &&
650             ' ' ==   string[11]  &&
651             isdigit (string[12]) &&  /* 2001 */
652             isdigit (string[13]) &&
653             isdigit (string[14]) &&
654             isdigit (string[15]) &&
655             ' ' ==   string[16]  &&
656             isdigit (string[17]) &&  /* 02: */
657             isdigit (string[18]) &&
658             ':' ==   string[19]  &&
659             isdigit (string[20]) &&  /* 59: */
660             isdigit (string[21]) &&
661             ':' ==   string[22]  &&
662             isdigit (string[23]) &&  /* 03 */
663             isdigit (string[24]) &&
664             ' ' ==   string[25]  &&
665             'G' ==   string[26]  &&  /* GMT */
666             'M' ==   string[27]  &&  /* GMT */
667             'T' ==   string[28]) {
668 
669         tm.tm_sec  = 10 * (string[23]-'0') + (string[24]-'0');
670         tm.tm_min  = 10 * (string[20]-'0') + (string[21]-'0');
671         tm.tm_hour = 10 * (string[17]-'0') + (string[18]-'0');
672         tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5]-'0') + (string[6]-'0');
673         tm.tm_mon = (!strncmp (string+8, "Jan", 3) ? 0 :
674                      !strncmp (string+8, "Feb", 3) ? 1 :
675                      !strncmp (string+8, "Mar", 3) ? 2 :
676                      !strncmp (string+8, "Apr", 3) ? 3 :
677                      !strncmp (string+8, "May", 3) ? 4 :
678                      !strncmp (string+8, "Jun", 3) ? 5 :
679                      !strncmp (string+8, "Jul", 3) ? 6 :
680                      !strncmp (string+8, "Aug", 3) ? 7 :
681                      !strncmp (string+8, "Sep", 3) ? 8 :
682                      !strncmp (string+8, "Oct", 3) ? 9 :
683                      !strncmp (string+8, "Nov", 3) ? 10 :
684                      !strncmp (string+8, "Dec", 3) ? 11 :
685                      -1);
686         tm.tm_year = ((1000 * (string[12]-'0') +
687                        100 * (string[13]-'0') +
688                        10 * (string[14]-'0') +
689                        (string[15]-'0'))
690                       - 1900);
691 
692         tm.tm_isdst = 0;  /* GMT is never in DST, right? */
693 
694         if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31)
695             return 0;
696 
697         /*
698         This is actually wrong: we need to subtract the local timezone
699         offset from GMT from this value.  But, that's ok in this usage,
700         because we only comparing these two GMT dates against each other,
701         so it doesn't matter what time zone we parse them in.
702         */
703 
704         t = mktime (&tm);
705         if (t == (time_t) -1) t = 0;
706 
707         if (verbose) {
708             const char *s = string;
709             while (*s && *s != '\r' && *s != '\n')
710                 fputc (*s++, stdout);
711             printf (" ==> %lu\n", (unsigned long) t);
712         }
713 
714         return t;
715 
716     } else {
717         return 0;
718     }
719 }
720 
721 /* Checks if the server 'reply' is one of the expected 'statuscodes' */
722 static int
expected_statuscode(const char * reply,const char * statuscodes)723 expected_statuscode (const char *reply, const char *statuscodes)
724 {
725     char *expected, *code;
726     int result = 0;
727 
728     if ((expected = strdup (statuscodes)) == NULL)
729         die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
730 
731     for (code = strtok (expected, ","); code != NULL; code = strtok (NULL, ","))
732         if (strstr (reply, code) != NULL) {
733             result = 1;
734             break;
735         }
736 
737     free (expected);
738     return result;
739 }
740 
chunk_header(char ** buf)741 int chunk_header(char **buf)
742 {
743     int lth = strtol(*buf, buf, 16);
744 
745     if (lth <= 0)
746         return lth;
747 
748     while (**buf !='\0' && **buf != '\r' && **buf != '\n')
749         ++*buf;
750 
751     // soak up the leading CRLF
752     if (**buf && **buf == '\r' && *(++*buf) && **buf == '\n')
753       ++*buf;
754     else
755       die (STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse chunked body, invalid format\n"));
756 
757     return lth;
758 }
759 
760 char *
decode_chunked_page(const char * raw,char * dst)761 decode_chunked_page (const char *raw, char *dst)
762 {
763     int  chunksize;
764     char *raw_pos = (char*)raw;
765     char *dst_pos = (char*)dst;
766 
767     for (;;) {
768         if ((chunksize = chunk_header(&raw_pos)) == 0)
769             break;
770         if (chunksize < 0)
771             die (STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse chunked body, invalid chunk size\n"));
772 
773         memmove(dst_pos, raw_pos, chunksize);
774         raw_pos += chunksize;
775         dst_pos += chunksize;
776         *dst_pos = '\0';
777 
778         while (*raw_pos && (*raw_pos == '\r' || *raw_pos == '\n'))
779             raw_pos++;
780     }
781 
782     return dst;
783 }
784 
785 static char *
header_value(const char * headers,const char * header)786 header_value (const char *headers, const char *header)
787 {
788     char *s;
789     char *value;
790     const char *value_end;
791     int value_size;
792 
793     if (!(s = strcasestr(headers, header))) {
794         return NULL;
795     }
796 
797     s += strlen(header);
798 
799     while (*s && (isspace(*s) || *s == ':')) s++;
800     while (*s && isspace(*s)) s++;
801 
802     value_end = strchr(s, '\r');
803     if (!value_end)
804         value_end = strchr(s, '\n');
805     if (!value_end) {
806         // Turns out there's no newline after the header... So it's at the end!
807         value_end = s + strlen(s);
808     }
809 
810     value_size = value_end - s;
811 
812     value = malloc(value_size + 1);
813     if (!value) {
814         die (STATE_UNKNOWN, _("HTTP_UNKNOWN - Memory allocation error\n"));
815     }
816 
817     if (!strncpy(value, s, value_size)) {
818         die(STATE_UNKNOWN, _("HTTP_UNKNOWN - Memory copy failure\n"));
819     }
820     value[value_size] = '\0';
821 
822     return value;
823 }
824 
825 static int
chunked_transfer_encoding(const char * headers)826 chunked_transfer_encoding (const char *headers)
827 {
828     int result;
829     char *encoding = header_value(headers, "Transfer-Encoding");
830     if (!encoding) {
831         return 0;
832     }
833 
834     if (! strncmp(encoding, "chunked", sizeof("chunked"))) {
835         result = 1;
836     } else {
837         result = 0;
838     }
839 
840     free(encoding);
841     return result;
842 }
843 
844 static int
check_document_dates(const char * headers,char ** msg)845 check_document_dates (const char *headers, char **msg)
846 {
847     const char *s;
848     char *server_date = 0;
849     char *document_date = 0;
850     int date_result = STATE_OK;
851 
852     s = headers;
853     while (*s) {
854         const char *field = s;
855         const char *value = 0;
856 
857         /* Find the end of the header field */
858         while (*s && !isspace(*s) && *s != ':')
859             s++;
860 
861         /* Remember the header value, if any. */
862         if (*s == ':')
863             value = ++s;
864 
865         /* Skip to the end of the header, including continuation lines. */
866         while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
867             s++;
868 
869         /* Avoid stepping over end-of-string marker */
870         if (*s)
871             s++;
872 
873         /* Process this header. */
874         if (value && value > field+2) {
875             char *ff = (char *) malloc (value-field);
876             char *ss = ff;
877             while (field < value-1)
878                 *ss++ = tolower(*field++);
879             *ss++ = 0;
880 
881             if (!strcmp (ff, "date") || !strcmp (ff, "last-modified")) {
882                 const char *e;
883                 while (*value && isspace (*value))
884                     value++;
885                 for (e = value; *e && *e != '\r' && *e != '\n'; e++)
886                     ;
887                 ss = (char *) malloc (e - value + 1);
888                 strncpy (ss, value, e - value);
889                 ss[e - value] = 0;
890                 if (!strcmp (ff, "date")) {
891                     if (server_date) free (server_date);
892                     server_date = ss;
893                 } else {
894                     if (document_date) free (document_date);
895                     document_date = ss;
896                 }
897             }
898             free (ff);
899         }
900     }
901 
902     /* Done parsing the body.  Now check the dates we (hopefully) parsed.  */
903     if (!server_date || !*server_date) {
904         xasprintf (msg, _("%sServer date unknown, "), *msg);
905         date_result = max_state_alt(STATE_UNKNOWN, date_result);
906     } else if (!document_date || !*document_date) {
907         xasprintf (msg, _("%sDocument modification date unknown, "), *msg);
908         date_result = max_state_alt(STATE_CRITICAL, date_result);
909     } else {
910         time_t srv_data = parse_time_string (server_date);
911         time_t doc_data = parse_time_string (document_date);
912 
913         if (srv_data <= 0) {
914             xasprintf (msg, _("%sServer date \"%100s\" unparsable, "), *msg, server_date);
915             date_result = max_state_alt(STATE_CRITICAL, date_result);
916         } else if (doc_data <= 0) {
917             xasprintf (msg, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date);
918             date_result = max_state_alt(STATE_CRITICAL, date_result);
919         } else if (doc_data > srv_data + 30) {
920             xasprintf (msg, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data);
921             date_result = max_state_alt(STATE_CRITICAL, date_result);
922         } else if (doc_data < srv_data - maximum_age) {
923             int n = (srv_data - doc_data);
924             if (n > (60 * 60 * 24 * 2)) {
925                 xasprintf (msg, _("%sLast modified %.1f days ago, "), *msg, ((float) n) / (60 * 60 * 24));
926                 date_result = max_state_alt(STATE_CRITICAL, date_result);
927             } else {
928                 xasprintf (msg, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60);
929                 date_result = max_state_alt(STATE_CRITICAL, date_result);
930             }
931         }
932         free (server_date);
933         free (document_date);
934     }
935     return date_result;
936 }
937 
938 int
get_content_length(const char * headers)939 get_content_length (const char *headers)
940 {
941     const char *s;
942     int content_length = -1;
943 
944     s = headers;
945     while (*s) {
946         const char *field = s;
947         const char *value = 0;
948 
949         /* Find the end of the header field */
950         while (*s && !isspace(*s) && *s != ':')
951             s++;
952 
953         /* Remember the header value, if any. */
954         if (*s == ':')
955             value = ++s;
956 
957         /* Skip to the end of the header, including continuation lines. */
958         while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
959             s++;
960 
961         /* Avoid stepping over end-of-string marker */
962         if (*s)
963             s++;
964 
965         /* Process this header. */
966         if (value && value > field+2) {
967             char *ff = (char *) malloc (value-field);
968             char *ss = ff;
969             while (field < value-1)
970                 *ss++ = tolower(*field++);
971             *ss++ = 0;
972 
973             if (!strcmp (ff, "content-length")) {
974                 const char *e;
975                 while (*value && isspace (*value))
976                     value++;
977                 for (e = value; *e && *e != '\r' && *e != '\n'; e++)
978                     ;
979                 ss = (char *) malloc (e - value + 1);
980                 strncpy (ss, value, e - value);
981                 ss[e - value] = 0;
982                 content_length = atoi(ss);
983                 free (ss);
984             }
985             free (ff);
986         }
987     }
988     return (content_length);
989 }
990 
991 char *
prepend_slash(char * path)992 prepend_slash (char *path)
993 {
994     char *newpath;
995 
996     if (path[0] == '/')
997         return path;
998 
999     if ((newpath = malloc (strlen(path) + 2)) == NULL)
1000         die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
1001     newpath[0] = '/';
1002     strcpy (newpath + 1, path);
1003     free (path);
1004     return newpath;
1005 }
1006 
1007 int
check_http(void)1008 check_http (void)
1009 {
1010     char *msg;
1011     char *status_line;
1012     char *status_code;
1013     char *header;
1014     char *page;
1015     char *auth;
1016     int http_status;
1017     int header_end;
1018     int content_length;
1019     int content_start;
1020     int seen_length;
1021     int i = 0;
1022     size_t pagesize = 0;
1023     char *full_page;
1024     char *full_page_new;
1025     char *buf;
1026     char *pos;
1027     long microsec = 0L;
1028     double elapsed_time = 0.0;
1029     long microsec_connect = 0L;
1030     double elapsed_time_connect = 0.0;
1031     long microsec_ssl = 0L;
1032     double elapsed_time_ssl = 0.0;
1033     long microsec_firstbyte = 0L;
1034     double elapsed_time_firstbyte = 0.0;
1035     long microsec_headers = 0L;
1036     double elapsed_time_headers = 0.0;
1037     long microsec_transfer = 0L;
1038     double elapsed_time_transfer = 0.0;
1039     int page_len = 0;
1040     int result = STATE_OK;
1041     char *force_host_header = NULL;
1042     int bad_response = FALSE;
1043     char save_char;
1044 
1045     /* try to connect to the host at the given port number */
1046     gettimeofday (&tv_temp, NULL);
1047     if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
1048         die (STATE_CRITICAL, _("HTTP CRITICAL - Unable to open TCP socket\n"));
1049     microsec_connect = deltime (tv_temp);
1050 
1051     /* if we are called with the -I option, the -j method is CONNECT and */
1052     /* we received -S for SSL, then we tunnel the request through a proxy*/
1053     /* @20100414, public[at]frank4dd.com, http://www.frank4dd.com/howto  */
1054 
1055     if ( server_address != NULL && strcmp(http_method, "CONNECT") == 0
1056             && host_name != NULL && use_ssl == TRUE) {
1057 
1058         if (verbose) printf ("Entering CONNECT tunnel mode with proxy %s:%d to dst %s:%d\n", server_address, server_port, host_name, HTTPS_PORT);
1059         asprintf (&buf, "%s %s:%d HTTP/1.1\r\n%s\r\n", http_method, host_name, HTTPS_PORT, user_agent);
1060         asprintf (&buf, "%sProxy-Connection: keep-alive\r\n", buf);
1061         asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
1062         /* we finished our request, send empty line with CRLF */
1063         asprintf (&buf, "%s%s", buf, CRLF);
1064         if (verbose) printf ("%s\n", buf);
1065         send(sd, buf, strlen (buf), 0);
1066         buf[0]='\0';
1067 
1068         if (verbose) printf ("Receive response from proxy\n");
1069         read (sd, buffer, MAX_INPUT_BUFFER-1);
1070         if (verbose) printf ("%s", buffer);
1071         /* Here we should check if we got HTTP/1.1 200 Connection established */
1072     }
1073 #ifdef HAVE_SSL
1074     elapsed_time_connect = (double)microsec_connect / 1.0e6;
1075     if (use_ssl == TRUE) {
1076         gettimeofday (&tv_temp, NULL);
1077         result = np_net_ssl_init_with_hostname_version_and_cert(sd, (use_sni ? host_name : NULL), ssl_version, client_cert, client_privkey);
1078         if (verbose) printf ("SSL initialized\n");
1079         if (result != STATE_OK)
1080             die (STATE_CRITICAL, NULL);
1081         microsec_ssl = deltime (tv_temp);
1082         elapsed_time_ssl = (double)microsec_ssl / 1.0e6;
1083         if (check_cert == TRUE) {
1084             result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit);
1085             if (continue_after_check_cert == FALSE) {
1086 
1087                 if (sd) {
1088                     close(sd);
1089                 }
1090                 np_net_ssl_cleanup();
1091                 return result;
1092             }
1093         }
1094     }
1095 #endif /* HAVE_SSL */
1096 
1097     if ( server_address != NULL && strcmp(http_method, "CONNECT") == 0
1098             && host_name != NULL && use_ssl == TRUE)
1099         asprintf (&buf, "%s %s %s\r\n%s\r\n", "GET", server_url, host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent);
1100     else
1101         asprintf (&buf, "%s %s %s\r\n%s\r\n", http_method, server_url, host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent);
1102 
1103     /* tell HTTP/1.1 servers not to keep the connection alive */
1104     xasprintf (&buf, "%sConnection: close\r\n", buf);
1105 
1106     /* check if Host header is explicitly set in options */
1107     if (http_opt_headers_count) {
1108         for (i = 0; i < http_opt_headers_count ; i++) {
1109             if (strncmp(http_opt_headers[i], "Host:", 5) == 0) {
1110                 force_host_header = http_opt_headers[i];
1111             }
1112         }
1113     }
1114 
1115     /* optionally send the host header info */
1116     if (host_name) {
1117         if (force_host_header) {
1118             xasprintf (&buf, "%s%s\r\n", buf, force_host_header);
1119         } else {
1120             /*
1121              * Specify the port only if we're using a non-default port (see RFC 2616,
1122              * 14.23).  Some server applications/configurations cause trouble if the
1123              * (default) port is explicitly specified in the "Host:" header line.
1124              */
1125             if ((use_ssl == FALSE && server_port == HTTP_PORT) ||
1126                     (use_ssl == TRUE && server_port == HTTPS_PORT))
1127                 xasprintf (&buf, "%sHost: %s\r\n", buf, host_name);
1128             else
1129                 xasprintf (&buf, "%sHost: %s:%d\r\n", buf, host_name, server_port);
1130         }
1131     }
1132 
1133     /* Inform server we accept any MIME type response
1134      * TODO: Take an argument to determine what type(s) to accept,
1135      * so that we can alert if a response is of an invalid type.
1136     */
1137     if (!have_accept)
1138         xasprintf(&buf, "%sAccept: */*\r\n", buf);
1139 
1140     /* optionally send any other header tag */
1141     if (http_opt_headers_count) {
1142         for (i = 0; i < http_opt_headers_count ; i++) {
1143             if (force_host_header != http_opt_headers[i]) {
1144                 xasprintf (&buf, "%s%s\r\n", buf, http_opt_headers[i]);
1145             }
1146         }
1147         /* This cannot be free'd here because a redirection will then try to access this and segfault */
1148         /* Covered in a testcase in tests/check_http.t */
1149         /* free(http_opt_headers); */
1150     }
1151 
1152     /* optionally send the authentication info */
1153     if (strlen(user_auth)) {
1154         base64_encode_alloc (user_auth, strlen (user_auth), &auth);
1155         xasprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
1156     }
1157 
1158     /* optionally send the proxy authentication info */
1159     if (strlen(proxy_auth)) {
1160         base64_encode_alloc (proxy_auth, strlen (proxy_auth), &auth);
1161         xasprintf (&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth);
1162     }
1163 
1164     /* either send http POST data (any data, not only POST)*/
1165     if (http_post_data) {
1166         if (http_content_type) {
1167             xasprintf (&buf, "%sContent-Type: %s\r\n", buf, http_content_type);
1168         } else {
1169             xasprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
1170         }
1171 
1172         xasprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, (int)strlen (http_post_data));
1173         xasprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
1174     } else {
1175         /* or just a newline so the server knows we're done with the request */
1176         xasprintf (&buf, "%s%s", buf, CRLF);
1177     }
1178 
1179     if (verbose) printf ("%s\n", buf);
1180     gettimeofday (&tv_temp, NULL);
1181     my_send (buf, strlen (buf));
1182     microsec_headers = deltime (tv_temp);
1183     elapsed_time_headers = (double)microsec_headers / 1.0e6;
1184 
1185     /* fetch the page */
1186     full_page = strdup("");
1187     gettimeofday (&tv_temp, NULL);
1188     while ((i = my_recv (buffer, MAX_INPUT_BUFFER-1)) > 0) {
1189         if ((i >= 1) && (elapsed_time_firstbyte <= 0.000001)) {
1190             microsec_firstbyte = deltime (tv_temp);
1191             elapsed_time_firstbyte = (double)microsec_firstbyte / 1.0e6;
1192         }
1193         buffer[i] = '\0';
1194         /* xasprintf (&full_page_new, "%s%s", full_page, buffer); */
1195         if ((full_page_new = realloc(full_page, pagesize + i + 1)) == NULL)
1196             die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate memory for full_page\n"));
1197 
1198         memmove(&full_page_new[pagesize], buffer, i + 1);
1199         /*free (full_page);*/
1200         full_page = full_page_new;
1201         pagesize += i;
1202 
1203         header_end = document_headers_done(full_page);
1204         if (header_end) {
1205             i = 0;
1206             break;
1207         }
1208     }
1209 
1210     if (no_body) {
1211         full_page[header_end] = '\0';
1212     }
1213     else {
1214         content_length = get_content_length(full_page);
1215 
1216         content_start = header_end + 1;
1217         while (full_page[content_start] == '\n' || full_page[content_start] == '\r') {
1218             content_start += 1;
1219         }
1220         seen_length = pagesize - content_start;
1221         /* Continue receiving the body until content-length is met */
1222         while ((content_length < 0 || seen_length < content_length)
1223             && ((i = my_recv(buffer, MAX_INPUT_BUFFER-1)) > 0)) {
1224 
1225             buffer[i] = '\0';
1226 
1227             if ((full_page_new = realloc(full_page, pagesize + i + 1)) == NULL)
1228                 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate memory for full_page\n"));
1229             memmove(&full_page_new[pagesize], buffer, i + 1);
1230             full_page = full_page_new;
1231 
1232             pagesize += i;
1233             seen_length = pagesize - content_start;
1234         }
1235     }
1236 
1237     microsec_transfer = deltime (tv_temp);
1238     elapsed_time_transfer = (double)microsec_transfer / 1.0e6;
1239 
1240     if (i < 0 && errno != ECONNRESET) {
1241 #ifdef HAVE_SSL
1242         /*
1243         if (use_ssl) {
1244           sslerr=SSL_get_error(ssl, i);
1245           if ( sslerr == SSL_ERROR_SSL ) {
1246             die (STATE_WARNING, _("HTTP WARNING - Client Certificate Required\n"));
1247           } else {
1248             die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
1249           }
1250         }
1251         else {
1252         */
1253 #endif
1254         die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
1255 #ifdef HAVE_SSL
1256         /* XXX
1257         }
1258         */
1259 #endif
1260     }
1261 
1262     /* return a CRITICAL status if we couldn't read any data */
1263     if (pagesize == (size_t) 0)
1264         die (STATE_CRITICAL, _("HTTP CRITICAL - No data received from host\n"));
1265 
1266     /* close the connection */
1267     if (sd) close(sd);
1268 #ifdef HAVE_SSL
1269     np_net_ssl_cleanup();
1270 #endif
1271 
1272     /* Save check time */
1273     microsec = deltime (tv);
1274     elapsed_time = (double)microsec / 1.0e6;
1275 
1276     /* leave full_page untouched so we can free it later */
1277     pos = page = full_page;
1278 
1279     if (verbose)
1280         printf ("%s://%s:%d%s is %d characters\n",
1281             use_ssl ? "https" : "http", server_address,
1282             server_port, server_url, (int)pagesize);
1283 
1284     /* find status line and null-terminate it */
1285     page += (size_t) strcspn (page, "\r\n");
1286     save_char = *page;
1287     *page = '\0';
1288     status_line = strdup(pos);
1289     *page = save_char;
1290     pos = page;
1291 
1292 
1293     strip (status_line);
1294     if (verbose)
1295         printf ("STATUS: %s\n", status_line);
1296 
1297     /* find header info and null-terminate it */
1298     header = page;
1299 
1300     for (;;) {
1301 
1302         if ((page == NULL) || !strncmp(page, "\r\n\r\n", 4) || !strncmp(page, "\n\n", 2) || *page == '\0' )
1303             break;
1304 
1305         while (*page == '\r' || *page == '\n') {
1306             ++page;
1307         }
1308 
1309         page += (size_t) strcspn (page, "\r\n");
1310         pos = page;
1311 
1312         if (*page == '\0')
1313             break;
1314     }
1315 
1316     page += (size_t) strspn (page, "\r\n");
1317     header[pos - header] = 0;
1318     while (*header == '\r' || *header == '\n') {
1319         ++header;
1320     }
1321 
1322     if (chunked_transfer_encoding(header) && *page)
1323         page = decode_chunked_page(page, page);
1324 
1325     if (verbose)
1326         printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header,
1327                 (no_body ? "  [[ skipped ]]" : page));
1328 
1329     xasprintf(&msg, "");
1330 
1331     /* make sure the status line matches the response we are looking for */
1332     if (!expected_statuscode (status_line, server_expect)) {
1333 
1334         if (server_port == HTTP_PORT)
1335             xasprintf (&msg,
1336                        _("Invalid HTTP response received from host: %s\n"),
1337                        status_line);
1338         else
1339             xasprintf (&msg,
1340                        _("Invalid HTTP response received from host on port %d: %s\n"),
1341                        server_port, status_line);
1342         bad_response = TRUE;
1343     }
1344 
1345     /* Bypass normal status line check if server_expect was set by user and not default */
1346     if ( server_expect_yn && !bad_response )  {
1347 
1348         xasprintf (&msg,
1349                    _("Status line output matched \"%s\" - "), server_expect);
1350 
1351         if (verbose)
1352             printf ("%s\n",msg);
1353 
1354     } else {
1355 
1356         /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
1357         /* HTTP-Version   = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
1358         /* Status-Code = 3 DIGITS */
1359 
1360         if (status_line != NULL) {
1361 
1362             status_code = strchr(status_line, ' ');
1363             if (status_code != NULL)
1364                 /* Normally the following line runs once, but some servers put extra whitespace between the version number and status code. */
1365                 while (*status_code == ' ') { status_code += sizeof(char); }
1366 
1367             if (status_code == NULL || (strspn(status_code, "1234567890") != 3))
1368                 check_http_die (STATE_CRITICAL, _("Invalid Status Line (%s)\n"), status_line);
1369 
1370         } else {
1371 
1372             check_http_die (STATE_CRITICAL, _("No Status Line\n"));
1373         }
1374 
1375         http_status = atoi (status_code);
1376 
1377         /* check the return code */
1378 
1379         if (http_status >= 600 || http_status < 100) {
1380             check_http_die (STATE_CRITICAL, _("Invalid Status (%s)\n"), status_line);
1381         }
1382 
1383         /* server errors result in a critical state */
1384         else if (http_status >= 500) {
1385             xasprintf (&msg, _("%s%s - "), msg, status_line);
1386             if (bad_response || !server_expect_yn)
1387                 result = STATE_CRITICAL;
1388         }
1389 
1390         /* client errors result in a warning state */
1391         else if (http_status >= 400) {
1392             xasprintf (&msg, _("%s%s - "), msg, status_line);
1393             if (bad_response || !server_expect_yn)
1394                 result = max_state_alt(STATE_WARNING, result);
1395         }
1396 
1397         /* check redirected page if specified */
1398         else if (http_status >= 300) {
1399 
1400             if (onredirect == STATE_DEPENDENT)
1401                 redir (header, status_line);
1402             else
1403                 result = max_state_alt(onredirect, result);
1404             xasprintf (&msg, _("%s%s - "), msg, status_line);
1405         }
1406 
1407         /* end if (http_status >= 300) */
1408         else if (!bad_response) {
1409 
1410             /* Print OK status anyway */
1411             xasprintf (&msg, _("%s%s - "), msg, status_line);
1412         }
1413 
1414     } /* end else [if (server_expect_yn)] */
1415 
1416     free(status_line);
1417 
1418     if (bad_response)
1419         check_http_die (STATE_CRITICAL, msg);
1420 
1421     /* reset the alarm - must be called *after* redir or we'll never die on redirects! */
1422     alarm (0);
1423 
1424     if (maximum_age >= 0) {
1425         result = max_state_alt(check_document_dates(header, &msg), result);
1426     }
1427 
1428 
1429     /* Page and Header content checks go here */
1430     if (strlen (header_expect)) {
1431         if (!strstr (header, header_expect)) {
1432             strncpy(&output_header_search[0],header_expect,sizeof(output_header_search));
1433             if(output_header_search[sizeof(output_header_search)-1]!='\0') {
1434                 bcopy("...",&output_header_search[sizeof(output_header_search)-4],4);
1435             }
1436             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);
1437             result = STATE_CRITICAL;
1438         }
1439     }
1440 
1441     if (strlen (string_expect)) {
1442         if (!strstr (page, string_expect)) {
1443             strncpy(&output_string_search[0],string_expect,sizeof(output_string_search));
1444             if(output_string_search[sizeof(output_string_search)-1]!='\0') {
1445                 bcopy("...",&output_string_search[sizeof(output_string_search)-4],4);
1446             }
1447             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);
1448             result = STATE_CRITICAL;
1449         }
1450     }
1451 
1452     if (strlen (regexp)) {
1453         errcode = regexec (&preg, page, REGS, pmatch, 0);
1454         if ((errcode == 0 && invert_regex == 0) || (errcode == REG_NOMATCH && invert_regex == 1)) {
1455             /* OK - No-op to avoid changing the logic around it */
1456             result = max_state_alt(STATE_OK, result);
1457         } else if ((errcode == REG_NOMATCH && invert_regex == 0) || (errcode == 0 && invert_regex == 1)) {
1458             if (invert_regex == 0)
1459                 xasprintf (&msg, _("%spattern not found, "), msg);
1460             else
1461                 xasprintf (&msg, _("%spattern found, "), msg);
1462             result = STATE_CRITICAL;
1463         } else {
1464             /* FIXME: Shouldn't that be UNKNOWN? */
1465             regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1466             xasprintf (&msg, _("%sExecute Error: %s, "), msg, errbuf);
1467             result = STATE_CRITICAL;
1468         }
1469     }
1470 
1471     /* make sure the page is of an appropriate size */
1472     /* page_len = get_content_length(header); */
1473     /* FIXME: Will this work with -N ? IMHO we should use
1474      * get_content_length(header) and always check if it's different than the
1475      * returned pagesize
1476      */
1477     /* FIXME: IIRC pagesize returns headers - shouldn't we make
1478      * it == get_content_length(header) ??
1479      */
1480     page_len = pagesize;
1481     if ((max_page_len > 0) && (page_len > max_page_len)) {
1482         xasprintf (&msg, _("%spage size %d too large, "), msg, page_len);
1483         result = max_state_alt(STATE_WARNING, result);
1484     } else if ((min_page_len > 0) && (page_len < min_page_len)) {
1485         xasprintf (&msg, _("%spage size %d too small, "), msg, page_len);
1486         result = max_state_alt(STATE_WARNING, result);
1487     }
1488 
1489     /* Cut-off trailing characters */
1490     if(msg[strlen(msg)-2] == ',') {
1491         msg[strlen(msg)-2] = '\0';
1492     }
1493     else {
1494         msg[strlen(msg)-3] = '\0';
1495     }
1496 
1497     /* show checked URL */
1498     if (show_url)
1499         xasprintf (&msg, _("%s - %s://%s:%d%s"), msg, use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url);
1500 
1501 
1502 
1503     /* check elapsed time */
1504     if (show_extended_perfdata) {
1505         xasprintf (&msg,
1506                    _("%s - %d bytes in %.3f second response time %s|%s %s %s %s %s %s %s"),
1507                    msg, page_len, elapsed_time,
1508                    (display_html ? "</A>" : ""),
1509                    perfd_time (elapsed_time),
1510                    perfd_size (page_len),
1511                    perfd_time_connect (elapsed_time_connect),
1512                    use_ssl == TRUE ? perfd_time_ssl (elapsed_time_ssl) : "",
1513                    perfd_time_headers (elapsed_time_headers),
1514                    perfd_time_firstbyte (elapsed_time_firstbyte),
1515                    perfd_time_transfer (elapsed_time_transfer));
1516     }
1517     else {
1518         xasprintf (&msg,
1519                    _("%s - %d bytes in %.3f second response time %s|%s %s"),
1520                    msg, page_len, elapsed_time,
1521                    (display_html ? "</A>" : ""),
1522                    perfd_time (elapsed_time),
1523                    perfd_size (page_len));
1524     }
1525 
1526     result = max_state_alt(get_status(elapsed_time, thlds), result);
1527 
1528     die (result, "HTTP %s: %s\n", state_text(result), msg);
1529 
1530     /* die failed? */
1531     return STATE_UNKNOWN;
1532 }
1533 
1534 
1535 
1536 /* per RFC 2396 */
1537 #define URI_HTTP "%5[HTPShtps]"
1538 #define URI_HOST "%255[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1539 #define URI_PORT "%6d" /* MAX_PORT's width is 5 chars, 6 to detect overflow */
1540 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1541 #define HD1 URI_HTTP "://" URI_HOST ":" URI_PORT "/" URI_PATH
1542 #define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH
1543 #define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT
1544 #define HD4 URI_HTTP "://" URI_HOST
1545 /* HD5 - relative reference redirect like //www.site.org/test https://tools.ietf.org/html/rfc3986 */
1546 #define HD5 URI_HTTP "//" URI_HOST "/" URI_PATH
1547 #define HD6 URI_PATH
1548 
1549 void
redir(char * pos,char * status_line)1550 redir (char *pos, char *status_line)
1551 {
1552     int i = 0;
1553     char *x;
1554     char xx[2];
1555     char type[6];
1556     char *addr;
1557     char *url;
1558 
1559     addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
1560     if (addr == NULL)
1561         die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate addr\n"));
1562 
1563     memset(addr, 0, MAX_IPV4_HOSTLENGTH);
1564     url = malloc (strcspn (pos, "\r\n"));
1565     if (url == NULL)
1566         die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1567 
1568     while (pos) {
1569         sscanf (pos, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx, &i);
1570         if (i == 0) {
1571             pos += (size_t) strcspn (pos, "\r\n");
1572             pos += (size_t) strspn (pos, "\r\n");
1573             if (strlen(pos) == 0)
1574                 die (STATE_UNKNOWN,
1575                      _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"),
1576                      status_line, (display_html ? "</A>" : ""));
1577             continue;
1578         }
1579 
1580         pos += i;
1581         pos += strspn (pos, " \t");
1582 
1583         /*
1584          * RFC 2616 (4.2):  ``Header fields can be extended over multiple lines by
1585          * preceding each extra line with at least one SP or HT.''
1586          */
1587         for (; (i = strspn (pos, "\r\n")); pos += i) {
1588             pos += i;
1589             if (!(i = strspn (pos, " \t"))) {
1590                 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Empty redirect location%s\n"),
1591                      display_html ? "</A>" : "");
1592             }
1593         }
1594 
1595         url = realloc (url, strcspn (pos, "\r\n") + 1);
1596         if (url == NULL)
1597             die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1598 
1599         /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
1600         if (sscanf (pos, HD1, type, addr, &i, url) == 4) {
1601             url = prepend_slash (url);
1602             use_ssl = server_type_check (type);
1603         }
1604 
1605         /* URI_HTTP URI_HOST URI_PATH */
1606         else if (sscanf (pos, HD2, type, addr, url) == 3 ) {
1607             url = prepend_slash (url);
1608             use_ssl = server_type_check (type);
1609             i = server_port_check (use_ssl);
1610         }
1611 
1612         /* URI_HTTP URI_HOST URI_PORT */
1613         else if (sscanf (pos, HD3, type, addr, &i) == 3) {
1614             strcpy (url, HTTP_URL);
1615             use_ssl = server_type_check (type);
1616         }
1617 
1618         /* URI_HTTP URI_HOST */
1619         else if (sscanf (pos, HD4, type, addr) == 2) {
1620             strcpy (url, HTTP_URL);
1621             use_ssl = server_type_check (type);
1622             i = server_port_check (use_ssl);
1623         }
1624 
1625         /* URI_HTTP, URI_HOST, URI_PATH */
1626         else if (sscanf (pos, HD5, addr, url) == 2) {
1627             if(use_ssl)
1628                 strcpy (type,"https");
1629             else
1630                 strcpy (type,server_type);
1631             xasprintf(&url, "/%s", url);
1632             use_ssl = server_type_check (type);
1633             i = server_port_check (use_ssl);
1634         }
1635 
1636         /* URI_PATH */
1637         else if (sscanf (pos, HD6, url) == 1) {
1638             /* relative url */
1639             if ((url[0] != '/')) {
1640                 if ((x = strrchr(server_url, '/')))
1641                     *x = '\0';
1642                 xasprintf (&url, "%s/%s", server_url, url);
1643             }
1644             i = server_port;
1645             strcpy (type, server_type);
1646             strcpy (addr, host_name ? host_name : server_address);
1647         }
1648 
1649         else {
1650             die (STATE_UNKNOWN,
1651                  _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"),
1652                  pos, (display_html ? "</A>" : ""));
1653         }
1654 
1655         break;
1656 
1657     } /* end while (pos) */
1658 
1659     if (++redir_depth > max_depth)
1660         die (STATE_WARNING,
1661              _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
1662              max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
1663 
1664     if (server_port==i &&
1665             !strncmp(server_address, addr, MAX_IPV4_HOSTLENGTH) &&
1666             (host_name && !strncmp(host_name, addr, MAX_IPV4_HOSTLENGTH)) &&
1667             !strcmp(server_url, url))
1668         die (STATE_WARNING,
1669              _("HTTP WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1670              type, addr, i, url, (display_html ? "</A>" : ""));
1671 
1672     strcpy (server_type, type);
1673 
1674     free (host_name);
1675     host_name = strndup (addr, MAX_IPV4_HOSTLENGTH);
1676 
1677     if (!(followsticky & STICKY_HOST)) {
1678         free (server_address);
1679         server_address = strndup (addr, MAX_IPV4_HOSTLENGTH);
1680     }
1681     if (!(followsticky & STICKY_PORT)) {
1682         server_port = i;
1683     }
1684 
1685     free (server_url);
1686     server_url = url;
1687 
1688     if (server_port > MAX_PORT)
1689         die (STATE_UNKNOWN,
1690              _("HTTP UNKNOWN - Redirection to port above %d - %s://%s:%d%s%s\n"),
1691              MAX_PORT, server_type, server_address, server_port, server_url,
1692              display_html ? "</A>" : "");
1693 
1694     if (verbose)
1695         printf (_("Redirection to %s://%s:%d%s\n"), server_type,
1696                 host_name ? host_name : server_address, server_port, server_url);
1697 
1698     free(addr);
1699     check_http ();
1700 }
1701 
1702 
1703 int
server_type_check(const char * type)1704 server_type_check (const char *type)
1705 {
1706     if (strcmp (type, "https"))
1707         return FALSE;
1708     else
1709         return TRUE;
1710 }
1711 
1712 int
server_port_check(int ssl_flag)1713 server_port_check (int ssl_flag)
1714 {
1715     if (ssl_flag)
1716         return HTTPS_PORT;
1717     else
1718         return HTTP_PORT;
1719 }
1720 
perfd_time(double elapsed_time)1721 char *perfd_time (double elapsed_time)
1722 {
1723     return fperfdata ("time", elapsed_time, "s",
1724                       thlds->warning?TRUE:FALSE, thlds->warning?thlds->warning->end:0,
1725                       thlds->critical?TRUE:FALSE, thlds->critical?thlds->critical->end:0,
1726                       TRUE, 0, FALSE, 0);
1727 }
1728 
perfd_time_connect(double elapsed_time_connect)1729 char *perfd_time_connect (double elapsed_time_connect)
1730 {
1731     return fperfdata ("time_connect", elapsed_time_connect, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0);
1732 }
1733 
perfd_time_ssl(double elapsed_time_ssl)1734 char *perfd_time_ssl (double elapsed_time_ssl)
1735 {
1736     return fperfdata ("time_ssl", elapsed_time_ssl, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0);
1737 }
1738 
perfd_time_headers(double elapsed_time_headers)1739 char *perfd_time_headers (double elapsed_time_headers)
1740 {
1741     return fperfdata ("time_headers", elapsed_time_headers, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0);
1742 }
1743 
perfd_time_firstbyte(double elapsed_time_firstbyte)1744 char *perfd_time_firstbyte (double elapsed_time_firstbyte)
1745 {
1746     return fperfdata ("time_firstbyte", elapsed_time_firstbyte, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0);
1747 }
1748 
perfd_time_transfer(double elapsed_time_transfer)1749 char *perfd_time_transfer (double elapsed_time_transfer)
1750 {
1751     return fperfdata ("time_transfer", elapsed_time_transfer, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0);
1752 }
1753 
perfd_size(int page_len)1754 char *perfd_size (int page_len)
1755 {
1756     return perfdata ("size", page_len, "B",
1757                      (min_page_len>0?TRUE:FALSE), min_page_len,
1758                      (min_page_len>0?TRUE:FALSE), 0,
1759                      TRUE, 0, FALSE, 0);
1760 }
1761 
1762 void
print_help(void)1763 print_help (void)
1764 {
1765     print_revision (progname, NP_VERSION);
1766 
1767     printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1768     printf (COPYRIGHT, copyright, email);
1769 
1770     printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
1771     printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
1772     printf ("%s\n", _("strings and regular expressions, check connection times, and report on"));
1773     printf ("%s\n", _("certificate expiration times."));
1774 
1775     printf ("\n\n");
1776 
1777     print_usage ();
1778 
1779     printf (_("NOTE: One or both of -H and -I must be specified"));
1780 
1781     printf ("\n");
1782 
1783     printf (UT_HELP_VRSN);
1784     printf (UT_EXTRA_OPTS);
1785 
1786     printf (" %s\n", "-H, --hostname=ADDRESS");
1787     printf ("    %s\n", _("Host name argument for servers using host headers (virtual host)"));
1788     printf ("    %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1789     printf (" %s\n", "-I, --IP-address=ADDRESS");
1790     printf ("    %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1791     printf (" %s\n", "-p, --port=INTEGER");
1792     printf ("    %s", _("Port number (default: "));
1793     printf ("%d)\n", HTTP_PORT);
1794 
1795     printf (UT_IPv46);
1796 
1797 #ifdef HAVE_SSL
1798     printf (" %s\n", "-S, --ssl=VERSION[+]");
1799     printf ("    %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents"));
1800     printf ("    %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,"));
1801     printf ("    %s\n", _("1.2 = TLSv1.2). With a '+' suffix, newer versions are also accepted."));
1802     printf (" %s\n", "--sni");
1803     printf ("    %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
1804 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
1805     printf (" %s\n", "--verify-host");
1806     printf ("    %s\n", _("Verify SSL certificate is for the -H hostname (with --sni and -S)"));
1807 #endif
1808     printf (" %s\n", "-C, --certificate=INTEGER[,INTEGER]");
1809     printf ("    %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
1810     printf ("    %s\n", _("(When this option is used the URL is not checked by default. You can use"));
1811     printf ("    %s\n", _(" --continue-after-certificate to override this behavior)"));
1812     printf (" %s\n", "--continue-after-certificate");
1813     printf ("    %s\n", _("Allows the HTTP check to continue after performing the certificate check."));
1814     printf ("    %s\n", _("Does nothing unless -C is used."));
1815     printf (" %s\n", "-J, --client-cert=FILE");
1816     printf ("   %s\n", _("Name of file that contains the client certificate (PEM format)"));
1817     printf ("   %s\n", _("to be used in establishing the SSL session"));
1818     printf (" %s\n", "-K, --private-key=FILE");
1819     printf ("   %s\n", _("Name of file containing the private key (PEM format)"));
1820     printf ("   %s\n", _("matching the client certificate"));
1821 #endif
1822 
1823     printf (" %s\n", "-e, --expect=STRING");
1824     printf ("    %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1825     printf ("    %s", _("the first (status) line of the server response (default: "));
1826     printf ("%s)\n", HTTP_EXPECT);
1827     printf ("    %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1828     printf (" %s\n", "-d, --header-string=STRING");
1829     printf ("    %s\n", _("String to expect in the response headers"));
1830     printf (" %s\n", "-s, --string=STRING");
1831     printf ("    %s\n", _("String to expect in the content"));
1832     printf (" %s\n", "-u, --uri=PATH");
1833     printf ("    %s\n", _("URI to GET or POST (default: /)"));
1834     printf (" %s\n", "--url=PATH");
1835     printf ("    %s\n", _("(deprecated) URL to GET or POST (default: /)"));
1836     printf (" %s\n", "-P, --post=STRING");
1837     printf ("    %s\n", _("URL encoded http POST data"));
1838     printf (" %s\n", "-j, --method=STRING  (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT)");
1839     printf ("    %s\n", _("Set HTTP method."));
1840     printf (" %s\n", "-N, --no-body");
1841     printf ("    %s\n", _("Don't wait for document body: stop reading after headers."));
1842     printf ("    %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
1843     printf (" %s\n", "-M, --max-age=SECONDS");
1844     printf ("    %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
1845     printf ("    %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1846     printf (" %s\n", "-T, --content-type=STRING");
1847     printf ("    %s\n", _("specify Content-Type header media type when POSTing\n"));
1848 
1849     printf (" %s\n", "-l, --linespan");
1850     printf ("    %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1851     printf (" %s\n", "-r, --regex, --ereg=STRING");
1852     printf ("    %s\n", _("Search page for regex STRING"));
1853     printf (" %s\n", "-R, --eregi=STRING");
1854     printf ("    %s\n", _("Search page for case-insensitive regex STRING"));
1855     printf (" %s\n", "--invert-regex");
1856     printf ("    %s\n", _("Return CRITICAL if found, OK if not\n"));
1857 
1858     printf (" %s\n", "-a, --authorization=AUTH_PAIR");
1859     printf ("    %s\n", _("Username:password on sites with basic authentication"));
1860     printf (" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
1861     printf ("    %s\n", _("Username:password on proxy-servers with basic authentication"));
1862     printf (" %s\n", "-A, --useragent=STRING");
1863     printf ("    %s\n", _("String to be sent in http header as \"User Agent\""));
1864     printf (" %s\n", "-k, --header=STRING");
1865     printf ("    %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers"));
1866     printf (" %s\n", "-E, --extended-perfdata");
1867     printf ("    %s\n", _("Print additional performance data"));
1868     printf (" %s\n", "-U, --show-url");
1869     printf ("    %s\n", _("Print URL in msg output in plain text"));
1870     printf (" %s\n", "-L, --link");
1871     printf ("    %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1872     printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>");
1873     printf ("    %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1874     printf ("    %s\n", _("specified IP address. stickyport also ensures port stays the same."));
1875     printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1876     printf ("    %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1877 
1878     printf (UT_WARN_CRIT);
1879 
1880     printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1881 
1882     printf (UT_VERBOSE);
1883 
1884     printf ("\n");
1885     printf ("%s\n", _("Notes:"));
1886     printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1887     printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1888     printf (" %s\n", _("other errors return STATE_UNKNOWN.  Successful connects, but incorrect response"));
1889     printf (" %s\n", _("messages from the host result in STATE_WARNING return values.  If you are"));
1890     printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1891     printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
1892     printf (" %s\n", _("You may also need to give a FQDN or IP address using -I (or --IP-Address)."));
1893 
1894 #ifdef HAVE_SSL
1895     printf ("\n");
1896     printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
1897     printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
1898     printf (" %s\n", _("certificate is still valid for the specified number of days."));
1899     printf ("\n");
1900     printf (" %s\n", _("Please note that this plugin does not check if the presented server"));
1901     printf (" %s\n", _("certificate matches the hostname of the server, or if the certificate"));
1902     printf (" %s\n", _("has a valid chain of trust to one of the locally installed CAs."));
1903     printf ("\n");
1904     printf ("%s\n", _("Examples:"));
1905     printf (" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com");
1906     printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
1907     printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1908     printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1909     printf (" %s\n", _("a STATE_CRITICAL will be returned."));
1910     printf ("\n");
1911     printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 14");
1912     printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1913     printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1914     printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
1915     printf (" %s\n\n", _("the certificate is expired."));
1916     printf ("\n");
1917     printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 30,14");
1918     printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 30 days,"));
1919     printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1920     printf (" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned."));
1921     printf (" %s\n", _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days"));
1922 
1923     printf (" %s\n\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: ");
1924     printf (" %s\n", _("check_http -I 192.168.100.35 -p 80 -u https://www.verisign.com/ -S -j CONNECT -H www.verisign.com "));
1925     printf (" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> -S(sl) -j CONNECT -H <webserver>"));
1926     printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1927     printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1928     printf (" %s\n", _("a STATE_CRITICAL will be returned."));
1929 
1930 #endif
1931 
1932     printf (UT_SUPPORT);
1933 
1934 }
1935 
1936 
1937 
1938 void
print_usage(void)1939 print_usage (void)
1940 {
1941     printf ("%s\n", _("Usage:"));
1942     printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname);
1943     printf ("       [-J <client certificate file>] [-K <private key>]\n");
1944     printf ("       [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-U] [-a auth]\n");
1945     printf ("       [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport>]\n");
1946     printf ("       [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
1947     printf ("       [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
1948 
1949 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
1950     printf ("       [-A string] [-k string] [-S <version>] [--sni] [--verify-host]\n");
1951     printf ("       [-C <warn_age>[,<crit_age>]] [-T <content-type>] [-j method]\n");
1952 #else
1953     printf ("       [-A string] [-k string] [-S <version>] [--sni] [-C <warn_age>[,<crit_age>]]\n");
1954     printf ("       [-T <content-type>] [-j method]\n");
1955 #endif
1956 }
1957