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