~ubuntu-branches/ubuntu/precise/nagios-plugins/precise-proposed

« back to all changes in this revision

Viewing changes to plugins/check_http.c

  • Committer: Bazaar Package Importer
  • Author(s): Guido Trotter
  • Date: 2004-06-15 15:37:48 UTC
  • Revision ID: james.westby@ubuntu.com-20040615153748-pq7702qdzghqfcns
Tags: upstream-1.3.1.0
ImportĀ upstreamĀ versionĀ 1.3.1.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
 *
 
3
 * Program: HTTP plugin for Nagios
 
4
 * License: GPL
 
5
 *
 
6
 * License Information:
 
7
 *
 
8
 * This program is free software; you can redistribute it and/or modify
 
9
 * it under the terms of the GNU General Public License as published by
 
10
 * the Free Software Foundation; either version 2 of the License, or
 
11
 * (at your option) any later version.
 
12
 *
 
13
 * This program is distributed in the hope that it will be useful,
 
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
 * GNU General Public License for more details.
 
17
 *
 
18
 * You should have received a copy of the GNU General Public License
 
19
 * along with this program; if not, write to the Free Software
 
20
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
21
 *
 
22
 * $Id: check_http.c,v 1.24.2.4 2003/06/21 05:31:23 kdebisschop Exp $
 
23
 *
 
24
 *****************************************************************************/
 
25
 
 
26
const char *progname = "check_http";
 
27
#define REVISION "$Revision: 1.24.2.4 $"
 
28
#define COPYRIGHT "1999-2001"
 
29
#define AUTHORS "Ethan Galstad/Karl DeBisschop"
 
30
#define EMAIL "kdebisschop@users.sourceforge.net"
 
31
 
 
32
#include "config.h"
 
33
#include "common.h"
 
34
#include "netutils.h"
 
35
#include "utils.h"
 
36
 
 
37
#define SUMMARY "\
 
38
This plugin tests the HTTP service on the specified host. It can test\n\
 
39
normal (http) and secure (https) servers, follow redirects, search for\n\
 
40
strings and regular expressions, check connection times, and report on\n\
 
41
certificate expiration times.\n"
 
42
 
 
43
#define OPTIONS "\
 
44
(-H <vhost> | -I <IP-address>) [-u <uri>] [-p <port>]\n\
 
45
            [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
 
46
            [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
 
47
            [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n\
 
48
            [-P string]"
 
49
 
 
50
#define LONGOPTIONS "\
 
51
 -H, --hostname=ADDRESS\n\
 
52
    Host name argument for servers using host headers (virtual host)\n\
 
53
 -I, --IP-address=ADDRESS\n\
 
54
   IP address or name (use numeric address if possible to bypass DNS lookup).\n\
 
55
 -e, --expect=STRING\n\
 
56
   String to expect in first (status) line of server response (default: %s)\n\
 
57
   If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)\n\
 
58
 -s, --string=STRING\n\
 
59
   String to expect in the content\n\
 
60
 -u, --url=PATH\n\
 
61
   URL to GET or POST (default: /)\n\
 
62
 -p, --port=INTEGER\n\
 
63
   Port number (default: %d)\n\
 
64
 -P, --post=STRING\n\
 
65
   URL encoded http POST data\n\
 
66
 -w, --warning=INTEGER\n\
 
67
   Response time to result in warning status (seconds)\n\
 
68
 -c, --critical=INTEGER\n\
 
69
   Response time to result in critical status (seconds)\n\
 
70
 -t, --timeout=INTEGER\n\
 
71
   Seconds before connection times out (default: %d)\n\
 
72
 -a, --authorization=AUTH_PAIR\n\
 
73
   Username:password on sites with basic authentication\n\
 
74
 -L, --link=URL\n\
 
75
   Wrap output in HTML link (obsoleted by urlize)\n\
 
76
 -f, --onredirect=<ok|warning|critical|follow>\n\
 
77
   How to handle redirected pages\n%s%s\
 
78
 -v, --verbose\n\
 
79
    Show details for command-line debugging (do not use with nagios server)\n\
 
80
 -h, --help\n\
 
81
    Print detailed help screen\n\
 
82
 -V, --version\n\
 
83
    Print version information\n"
 
84
 
 
85
#ifdef HAVE_SSL
 
86
#define SSLOPTIONS "\
 
87
 -S, --ssl\n\
 
88
    Connect via SSL\n\
 
89
 -C, --certificate=INTEGER\n\
 
90
    Minimum number of days a certificate has to be valid.\n\
 
91
    (when this option is used the url is not checked.)\n"
 
92
#else
 
93
#define SSLOPTIONS ""
 
94
#endif
 
95
 
 
96
#ifdef HAVE_REGEX_H
 
97
#define REGOPTIONS "\
 
98
 -l, --linespan\n\
 
99
    Allow regex to span newlines (must precede -r or -R)\n\
 
100
 -r, --regex, --ereg=STRING\n\
 
101
    Search page for regex STRING\n\
 
102
 -R, --eregi=STRING\n\
 
103
    Search page for case-insensitive regex STRING\n"
 
104
#else
 
105
#define REGOPTIONS ""
 
106
#endif
 
107
 
 
108
#define DESCRIPTION "\
 
109
This plugin will attempt to open an HTTP connection with the host. Successul\n\
 
110
connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
 
111
errors return STATE_UNKNOWN.  Successful connects, but incorrect reponse\n\
 
112
messages from the host result in STATE_WARNING return values.  If you are\n\
 
113
checking a virtual server that uses \"host headers\" you must supply the FQDN\n\
 
114
\(fully qualified domain name) as the [host_name] argument.\n"
 
115
 
 
116
#define SSLDESCRIPTION "\
 
117
This plugin can also check whether an SSL enabled web server is able to\n\
 
118
serve content (optionally within a specified time) or whether the X509 \n\
 
119
certificate is still valid for the specified number of days.\n\n\
 
120
CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
 
121
When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
 
122
STATE_OK will be returned. When the server returns its content but exceeds\n\
 
123
the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
 
124
a STATE_CRITICAL will be returned.\n\n\
 
125
CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
 
126
When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
 
127
STATE_OK is returned. When the certificate is still valid, but for less than\n\
 
128
14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
 
129
the certificate is expired.\n"
 
130
 
 
131
#ifdef HAVE_SSL_H
 
132
#include <rsa.h>
 
133
#include <crypto.h>
 
134
#include <x509.h>
 
135
#include <pem.h>
 
136
#include <ssl.h>
 
137
#include <err.h>
 
138
#include <rand.h>
 
139
#endif
 
140
 
 
141
#ifdef HAVE_OPENSSL_SSL_H
 
142
#include <openssl/rsa.h>
 
143
#include <openssl/crypto.h>
 
144
#include <openssl/x509.h>
 
145
#include <openssl/pem.h>
 
146
#include <openssl/ssl.h>
 
147
#include <openssl/err.h>
 
148
#include <openssl/rand.h>
 
149
#endif
 
150
 
 
151
#ifdef HAVE_SSL
 
152
int check_cert = FALSE;
 
153
int days_till_exp;
 
154
char *randbuff = "";
 
155
SSL_CTX *ctx;
 
156
SSL *ssl;
 
157
X509 *server_cert;
 
158
int connect_SSL (void);
 
159
int check_certificate (X509 **);
 
160
#endif
 
161
 
 
162
#ifdef HAVE_REGEX_H
 
163
enum {
 
164
        REGS = 2,
 
165
        MAX_RE_SIZE = 256
 
166
};
 
167
#include <regex.h>
 
168
regex_t preg;
 
169
regmatch_t pmatch[REGS];
 
170
char regexp[MAX_RE_SIZE];
 
171
char errbuf[MAX_INPUT_BUFFER];
 
172
int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
 
173
int errcode;
 
174
#endif
 
175
 
 
176
struct timeval tv;
 
177
 
 
178
#define server_type_check(server_type) \
 
179
(strcmp (server_type, "https") ? FALSE : TRUE)
 
180
 
 
181
#define server_port_check(use_ssl) (use_ssl ? HTTPS_PORT : HTTP_PORT)
 
182
 
 
183
/* per RFC 2396 */
 
184
#define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
 
185
#define URI_HTTP "%[HTPShtps]://"
 
186
#define URI_HOST "%[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
 
187
#define URI_PORT ":%[0123456789]"
 
188
#define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
 
189
enum {
 
190
        MAX_IPV4_HOSTLENGTH = 255,
 
191
        HTTP_PORT = 80,
 
192
        HTTPS_PORT = 443
 
193
};
 
194
 
 
195
#define HTTP_EXPECT "HTTP/1."
 
196
#define HTTP_URL "/"
 
197
#define CRLF "\r\n"
 
198
 
 
199
char timestamp[17] = "";
 
200
int specify_port = FALSE;
 
201
int server_port = HTTP_PORT;
 
202
char server_port_text[6] = "";
 
203
char server_type[6] = "http";
 
204
char *server_address = ""; 
 
205
char *host_name = "";
 
206
char *server_url = "";
 
207
int server_url_length;
 
208
int server_expect_yn = 0;
 
209
char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
 
210
char string_expect[MAX_INPUT_BUFFER] = "";
 
211
double warning_time = 0;
 
212
int check_warning_time = FALSE;
 
213
double critical_time = 0;
 
214
int check_critical_time = FALSE;
 
215
char user_auth[MAX_INPUT_BUFFER] = "";
 
216
int display_html = FALSE;
 
217
int onredirect = STATE_OK;
 
218
int use_ssl = FALSE;
 
219
int verbose = FALSE;
 
220
int sd;
 
221
char *http_method = "GET";
 
222
char *http_post_data = "";
 
223
char buffer[MAX_INPUT_BUFFER];
 
224
 
 
225
void print_usage (void);
 
226
void print_help (void);
 
227
int process_arguments (int, char **);
 
228
static char *base64 (char *bin, int len);
 
229
int check_http (void);
 
230
int my_recv (void);
 
231
int my_close (void);
 
232
 
 
233
int
 
234
main (int argc, char **argv)
 
235
{
 
236
        int result = STATE_UNKNOWN;
 
237
 
 
238
        /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
 
239
        asprintf (&server_url, "%s", HTTP_URL);
 
240
        server_url_length = strlen(server_url);
 
241
 
 
242
        if (process_arguments (argc, argv) == ERROR)
 
243
                usage ("check_http: could not parse arguments\n");
 
244
 
 
245
        if (strstr (timestamp, ":")) {
 
246
                if (strstr (server_url, "?"))
 
247
                        asprintf (&server_url, "%s&%s", server_url, timestamp);
 
248
                else
 
249
                        asprintf (&server_url, "%s?%s", server_url, timestamp);
 
250
        }
 
251
 
 
252
        if (display_html == TRUE)
 
253
                printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
 
254
                        host_name, server_port, server_url);
 
255
 
 
256
        /* initialize alarm signal handling, set socket timeout, start timer */
 
257
        (void) signal (SIGALRM, socket_timeout_alarm_handler);
 
258
        (void) alarm (socket_timeout);
 
259
        gettimeofday (&tv, NULL);
 
260
 
 
261
#ifdef HAVE_SSL
 
262
        if (use_ssl && check_cert == TRUE) {
 
263
                if (connect_SSL () != OK)
 
264
                        terminate (STATE_CRITICAL,
 
265
                                   "HTTP CRITICAL - Could not make SSL connection\n");
 
266
                if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
 
267
                        result = check_certificate (&server_cert);
 
268
                        X509_free (server_cert);
 
269
                }
 
270
                else {
 
271
                        printf ("ERROR: Cannot retrieve server certificate.\n");
 
272
                        result = STATE_CRITICAL;
 
273
                }
 
274
                SSL_shutdown (ssl);
 
275
                SSL_free (ssl);
 
276
                SSL_CTX_free (ctx);
 
277
                close (sd);
 
278
        }
 
279
        else {
 
280
                result = check_http ();
 
281
        }
 
282
#else
 
283
        result = check_http ();
 
284
#endif
 
285
        return result;
 
286
}
 
287
 
 
288
 
 
289
 
 
290
/* process command-line arguments */
 
291
int
 
292
process_arguments (int argc, char **argv)
 
293
{
 
294
        int c = 1;
 
295
 
 
296
#ifdef HAVE_GETOPT_H
 
297
        int option_index = 0;
 
298
        static struct option long_options[] = {
 
299
                STD_LONG_OPTS,
 
300
                {"file",required_argument,0,'F'},
 
301
                {"link", no_argument, 0, 'L'},
 
302
                {"nohtml", no_argument, 0, 'n'},
 
303
                {"ssl", no_argument, 0, 'S'},
 
304
                {"verbose", no_argument, 0, 'v'},
 
305
                {"post", required_argument, 0, 'P'},
 
306
                {"IP-address", required_argument, 0, 'I'},
 
307
                {"string", required_argument, 0, 's'},
 
308
                {"regex", required_argument, 0, 'r'},
 
309
                {"ereg", required_argument, 0, 'r'},
 
310
                {"eregi", required_argument, 0, 'R'},
 
311
                {"linespan", no_argument, 0, 'l'},
 
312
                {"onredirect", required_argument, 0, 'f'},
 
313
                {"certificate", required_argument, 0, 'C'},
 
314
                {0, 0, 0, 0}
 
315
        };
 
316
#endif
 
317
 
 
318
        if (argc < 2)
 
319
                return ERROR;
 
320
 
 
321
        for (c = 1; c < argc; c++) {
 
322
                if (strcmp ("-to", argv[c]) == 0)
 
323
                        strcpy (argv[c], "-t");
 
324
                if (strcmp ("-hn", argv[c]) == 0)
 
325
                        strcpy (argv[c], "-H");
 
326
                if (strcmp ("-wt", argv[c]) == 0)
 
327
                        strcpy (argv[c], "-w");
 
328
                if (strcmp ("-ct", argv[c]) == 0)
 
329
                        strcpy (argv[c], "-c");
 
330
                if (strcmp ("-nohtml", argv[c]) == 0)
 
331
                        strcpy (argv[c], "-n");
 
332
        }
 
333
 
 
334
#define OPTCHARS "Vvht:c:w:H:P:I:a:e:p:s:R:r:u:f:C:nlLS"
 
335
 
 
336
        while (1) {
 
337
#ifdef HAVE_GETOPT_H
 
338
                c = getopt_long (argc, argv, OPTCHARS, long_options, &option_index);
 
339
#else
 
340
                c = getopt (argc, argv, OPTCHARS);
 
341
#endif
 
342
                if (c == -1 || c == EOF)
 
343
                        break;
 
344
 
 
345
                switch (c) {
 
346
                case '?': /* usage */
 
347
                        usage2 ("unknown argument", optarg);
 
348
                        break;
 
349
                case 'h': /* help */
 
350
                        print_help ();
 
351
                        exit (STATE_OK);
 
352
                        break;
 
353
                case 'V': /* version */
 
354
                        print_revision (progname, REVISION);
 
355
                        exit (STATE_OK);
 
356
                        break;
 
357
                case 't': /* timeout period */
 
358
                        if (!is_intnonneg (optarg))
 
359
                                usage2 ("timeout interval must be a non-negative integer", optarg);
 
360
                        socket_timeout = atoi (optarg);
 
361
                        break;
 
362
                case 'c': /* critical time threshold */
 
363
                        if (!is_intnonneg (optarg))
 
364
                                usage2 ("invalid critical threshold", optarg);
 
365
                        critical_time = strtod (optarg, NULL);
 
366
                        check_critical_time = TRUE;
 
367
                        break;
 
368
                case 'w': /* warning time threshold */
 
369
                        if (!is_intnonneg (optarg))
 
370
                                usage2 ("invalid warning threshold", optarg);
 
371
                        warning_time = strtod (optarg, NULL);
 
372
                        check_warning_time = TRUE;
 
373
                        break;
 
374
                case 'L': /* show html link */
 
375
                        display_html = TRUE;
 
376
                        break;
 
377
                case 'n': /* do not show html link */
 
378
                        display_html = FALSE;
 
379
                        break;
 
380
                case 'S': /* use SSL */
 
381
#ifndef HAVE_SSL
 
382
                        usage ("check_http: invalid option - SSL is not available\n");
 
383
#endif
 
384
                        use_ssl = TRUE;
 
385
                        if (specify_port == FALSE)
 
386
                                server_port = HTTPS_PORT;
 
387
                        break;
 
388
                case 'C': /* Check SSL cert validity */
 
389
#ifdef HAVE_SSL
 
390
                        if (!is_intnonneg (optarg))
 
391
                                usage2 ("invalid certificate expiration period", optarg);
 
392
                        days_till_exp = atoi (optarg);
 
393
                        check_cert = TRUE;
 
394
#else
 
395
                        usage ("check_http: invalid option - SSL is not available\n");
 
396
#endif
 
397
                        break;
 
398
                case 'f': /* onredirect */
 
399
                        if (!strcmp (optarg, "follow"))
 
400
                                onredirect = STATE_DEPENDENT;
 
401
                        if (!strcmp (optarg, "unknown"))
 
402
                                onredirect = STATE_UNKNOWN;
 
403
                        if (!strcmp (optarg, "ok"))
 
404
                                onredirect = STATE_OK;
 
405
                        if (!strcmp (optarg, "warning"))
 
406
                                onredirect = STATE_WARNING;
 
407
                        if (!strcmp (optarg, "critical"))
 
408
                                onredirect = STATE_CRITICAL;
 
409
                        if (verbose)
 
410
                                printf("option f:%d \n", onredirect);  
 
411
                        break;
 
412
                /* Note: H, I, and u must be malloc'd or will fail on redirects */
 
413
                case 'H': /* Host Name (virtual host) */
 
414
                        asprintf (&host_name, "%s", optarg);
 
415
                        break;
 
416
                case 'I': /* Server IP-address */
 
417
                        asprintf (&server_address, "%s", optarg);
 
418
                        break;
 
419
                case 'u': /* Host or server */
 
420
                        asprintf (&server_url, "%s", optarg);
 
421
                        server_url_length = strlen (server_url);
 
422
                        break;
 
423
                case 'p': /* Host or server */
 
424
                        if (!is_intnonneg (optarg))
 
425
                                usage2 ("invalid port number", optarg);
 
426
                        server_port = atoi (optarg);
 
427
                        specify_port = TRUE;
 
428
                        break;
 
429
                case 'a': /* authorization info */
 
430
                        strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
 
431
                        user_auth[MAX_INPUT_BUFFER - 1] = 0;
 
432
                        break;
 
433
                case 'P': /* HTTP POST data in URL encoded format */
 
434
                        asprintf (&http_method, "%s", "POST");
 
435
                        asprintf (&http_post_data, "%s", optarg);
 
436
                        break;
 
437
                case 's': /* string or substring */
 
438
                        strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
 
439
                        string_expect[MAX_INPUT_BUFFER - 1] = 0;
 
440
                        break;
 
441
                case 'e': /* string or substring */
 
442
                        strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
 
443
                        server_expect[MAX_INPUT_BUFFER - 1] = 0;
 
444
                        server_expect_yn = 1;
 
445
                        break;
 
446
#ifndef HAVE_REGEX_H
 
447
                case 'l': /* linespan */
 
448
                case 'r': /* linespan */
 
449
                case 'R': /* linespan */
 
450
                        usage ("check_http: call for regex which was not a compiled option\n");
 
451
                        break;
 
452
#else
 
453
                case 'l': /* linespan */
 
454
                        cflags &= ~REG_NEWLINE;
 
455
                        break;
 
456
                case 'R': /* regex */
 
457
                        cflags |= REG_ICASE;
 
458
                case 'r': /* regex */
 
459
                        strncpy (regexp, optarg, MAX_RE_SIZE - 1);
 
460
                        regexp[MAX_RE_SIZE - 1] = 0;
 
461
                        errcode = regcomp (&preg, regexp, cflags);
 
462
                        if (errcode != 0) {
 
463
                                (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
 
464
                                printf ("Could Not Compile Regular Expression: %s", errbuf);
 
465
                                return ERROR;
 
466
                        }
 
467
                        break;
 
468
#endif
 
469
                case 'v': /* verbose */
 
470
                        verbose = TRUE;
 
471
                        break;
 
472
                }
 
473
        }
 
474
 
 
475
        c = optind;
 
476
 
 
477
        if (strcmp (server_address, "") == 0 && c < argc)
 
478
                        asprintf (&server_address, "%s", argv[c++]);
 
479
 
 
480
        if (strcmp (host_name, "") == 0 && c < argc)
 
481
                asprintf (&host_name, "%s", argv[c++]);
 
482
 
 
483
        if (strcmp (server_address ,"") == 0) {
 
484
                if (strcmp (host_name, "") == 0)
 
485
                        usage ("check_http: you must specify a server address or host name\n");
 
486
                else
 
487
                        asprintf (&server_address, "%s", host_name);
 
488
        }
 
489
 
 
490
        return TRUE;
 
491
}
 
492
 
 
493
 
 
494
 
 
495
/* written by lauri alanko */
 
496
static char *
 
497
base64 (char *bin, int len)
 
498
{
 
499
 
 
500
        char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
 
501
        int i = 0, j = 0;
 
502
 
 
503
        char BASE64_END = '=';
 
504
        char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
505
 
 
506
        while (j < len - 2) {
 
507
                buf[i++] = base64_table[bin[j] >> 2];
 
508
                buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
 
509
                buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
 
510
                buf[i++] = base64_table[bin[j + 2] & 63];
 
511
                j += 3;
 
512
        }
 
513
 
 
514
        switch (len - j) {
 
515
        case 1:
 
516
                buf[i++] = base64_table[bin[j] >> 2];
 
517
                buf[i++] = base64_table[(bin[j] & 3) << 4];
 
518
                buf[i++] = BASE64_END;
 
519
                buf[i++] = BASE64_END;
 
520
                break;
 
521
        case 2:
 
522
                buf[i++] = base64_table[bin[j] >> 2];
 
523
                buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
 
524
                buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
 
525
                buf[i++] = BASE64_END;
 
526
                break;
 
527
        case 0:
 
528
                break;
 
529
        }
 
530
 
 
531
        buf[i] = '\0';
 
532
        return buf;
 
533
}
 
534
 
 
535
 
 
536
 
 
537
int
 
538
check_http (void)
 
539
{
 
540
        char *msg = NULL;
 
541
        char *status_line = "";
 
542
        char *header = NULL;
 
543
        char *page = "";
 
544
        char *auth = NULL;
 
545
        int i = 0;
 
546
        size_t pagesize = 0;
 
547
        char *full_page = "";
 
548
        char *buf = NULL;
 
549
        char *pos = "";
 
550
        char *x = NULL;
 
551
        char *orig_url = NULL;
 
552
        double elapsed_time;
 
553
#ifdef HAVE_SSL
 
554
        int sslerr;
 
555
#endif
 
556
 
 
557
        /* try to connect to the host at the given port number */
 
558
#ifdef HAVE_SSL
 
559
        if (use_ssl == TRUE) {
 
560
 
 
561
                if (connect_SSL () != OK) {
 
562
                        terminate (STATE_CRITICAL, "Unable to open TCP socket");
 
563
                }
 
564
 
 
565
                if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
 
566
                        X509_free (server_cert);
 
567
                }
 
568
                else {
 
569
                        printf ("ERROR: Cannot retrieve server certificate.\n");
 
570
                        return STATE_CRITICAL;
 
571
                }
 
572
 
 
573
        }
 
574
        else {
 
575
#endif
 
576
                if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
 
577
                        terminate (STATE_CRITICAL, "Unable to open TCP socket");
 
578
#ifdef HAVE_SSL
 
579
        }
 
580
#endif
 
581
 
 
582
        asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
 
583
 
 
584
        /* optionally send the host header info (not clear if it's usable) */
 
585
        if (strcmp (host_name, ""))
 
586
                asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
 
587
 
 
588
        /* send user agent */
 
589
        asprintf (&buf, "%sUser-Agent: check_http/%s (nagios-plugins %s)\r\n",
 
590
                  buf, clean_revstring (REVISION), PACKAGE_VERSION);
 
591
 
 
592
        /* optionally send the authentication info */
 
593
        if (strcmp (user_auth, "")) {
 
594
                auth = base64 (user_auth, strlen (user_auth));
 
595
                asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
 
596
        }
 
597
 
 
598
        /* either send http POST data */
 
599
        if (strlen (http_post_data)) {
 
600
                asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
 
601
                asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
 
602
                asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
 
603
        }
 
604
        else {
 
605
                /* or just a newline so the server knows we're done with the request */
 
606
                asprintf (&buf, "%s%s", buf, CRLF);
 
607
        }
 
608
 
 
609
#ifdef HAVE_SSL
 
610
        if (use_ssl == TRUE) {
 
611
                if (SSL_write (ssl, buf, strlen (buf)) == -1) {
 
612
                        ERR_print_errors_fp (stderr);
 
613
                        return STATE_CRITICAL;
 
614
                }
 
615
        }
 
616
        else {
 
617
#endif
 
618
                send (sd, buf, strlen (buf), 0);
 
619
#ifdef HAVE_SSL
 
620
        }
 
621
#endif
 
622
 
 
623
        /* fetch the page */
 
624
        while ((i = my_recv ()) > 0) {
 
625
                buffer[i] = '\0';
 
626
                asprintf (&full_page, "%s%s", full_page, buffer);
 
627
                pagesize += i;
 
628
        }
 
629
 
 
630
        if (i < 0 && errno != ECONNRESET) {
 
631
#ifdef HAVE_SSL
 
632
                if (use_ssl) {
 
633
                        sslerr=SSL_get_error(ssl, i);
 
634
                        if ( sslerr == SSL_ERROR_SSL ) {
 
635
                                terminate (STATE_WARNING, "Client Certificate Required\n");
 
636
                        } else {
 
637
                                terminate (STATE_CRITICAL, "Error in recv()");
 
638
                        }
 
639
                }
 
640
                else {
 
641
#endif
 
642
                        terminate (STATE_CRITICAL, "Error in recv()");
 
643
#ifdef HAVE_SSL
 
644
                }
 
645
#endif
 
646
        }
 
647
 
 
648
        /* return a CRITICAL status if we couldn't read any data */
 
649
        if (pagesize == (size_t) 0)
 
650
                terminate (STATE_CRITICAL, "No data received %s", timestamp);
 
651
 
 
652
        /* close the connection */
 
653
        my_close ();
 
654
 
 
655
        /* reset the alarm */
 
656
        alarm (0);
 
657
 
 
658
        /* leave full_page untouched so we can free it later */
 
659
        page = full_page;
 
660
 
 
661
        if (verbose)
 
662
                printf ("Page is %d characters\n", pagesize);
 
663
 
 
664
        /* find status line and null-terminate it */
 
665
        status_line = page;
 
666
        page += (size_t) strcspn (page, "\r\n");
 
667
        pos = page;
 
668
        page += (size_t) strspn (page, "\r\n");
 
669
        status_line[strcspn(status_line, "\r\n")] = 0;
 
670
        strip (status_line);
 
671
        if (verbose)
 
672
                printf ("STATUS: %s\n", status_line);
 
673
 
 
674
        /* find header info and null terminate it */
 
675
        header = page;
 
676
        while (strcspn (page, "\r\n") > 0) {
 
677
                page += (size_t) strcspn (page, "\r\n");
 
678
                pos = page;
 
679
                if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
 
680
                    (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
 
681
                        page += (size_t) 2;
 
682
                else
 
683
                        page += (size_t) 1;
 
684
        }
 
685
        page += (size_t) strspn (page, "\r\n");
 
686
        header[pos - header] = 0;
 
687
        if (verbose)
 
688
                printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
 
689
 
 
690
        /* make sure the status line matches the response we are looking for */
 
691
        if (!strstr (status_line, server_expect)) {
 
692
                if (server_port == HTTP_PORT)
 
693
                        asprintf (&msg, "Invalid HTTP response received from host\n");
 
694
                else
 
695
                        asprintf (&msg,
 
696
                                        "Invalid HTTP response received from host on port %d\n",
 
697
                                        server_port);
 
698
                terminate (STATE_CRITICAL, msg);
 
699
        }
 
700
 
 
701
 
 
702
        /* Exit here if server_expect was set by user and not default */
 
703
        if ( server_expect_yn  )  {
 
704
                asprintf (&msg, "HTTP OK: Status line output matched \"%s\"\n",
 
705
                          server_expect);
 
706
                if (verbose)
 
707
                        printf ("%s\n",msg);
 
708
 
 
709
        }
 
710
        else {
 
711
        
 
712
 
 
713
                /* check the return code */
 
714
                /* server errors result in a critical state */
 
715
                if (strstr (status_line, "500") ||
 
716
                  strstr (status_line, "501") ||
 
717
                strstr (status_line, "502") ||
 
718
                    strstr (status_line, "503")) {
 
719
                        terminate (STATE_CRITICAL, "HTTP CRITICAL: %s\n", status_line);
 
720
                }
 
721
 
 
722
                /* client errors result in a warning state */
 
723
                if (strstr (status_line, "400") ||
 
724
                  strstr (status_line, "401") ||
 
725
                strstr (status_line, "402") ||
 
726
                    strstr (status_line, "403") ||
 
727
                    strstr (status_line, "404")) {
 
728
                        terminate (STATE_WARNING, "HTTP WARNING: %s\n", status_line);
 
729
                }
 
730
 
 
731
                /* check redirected page if specified */
 
732
                if (strstr (status_line, "300") ||
 
733
                  strstr (status_line, "301") ||
 
734
                strstr (status_line, "302") ||
 
735
                    strstr (status_line, "303") ||
 
736
                    strstr (status_line, "304")) {
 
737
                        if (onredirect == STATE_DEPENDENT) {
 
738
 
 
739
                                asprintf (&orig_url, "%s", server_url);
 
740
                                pos = header;
 
741
                                while (pos) {
 
742
                                        server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH + 1);
 
743
                                        if (server_address == NULL)
 
744
                                                terminate (STATE_UNKNOWN,
 
745
                                                                                 "HTTP UNKNOWN: could not allocate server_address");
 
746
                                        if (strcspn (pos, "\r\n") > server_url_length) {
 
747
                                                server_url = realloc (server_url, strcspn (pos, "\r\n"));
 
748
                                                if (server_url == NULL)
 
749
                                                        terminate (STATE_UNKNOWN,
 
750
                                                                   "HTTP UNKNOWN: could not allocate server_url");
 
751
                                                server_url_length = strcspn (pos, "\r\n");
 
752
                                        }
 
753
                                        if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
 
754
                                                asprintf (&host_name, "%s", server_address);
 
755
                                                use_ssl = server_type_check (server_type);
 
756
                                                server_port = atoi (server_port_text);
 
757
                                                check_http ();
 
758
                                        }
 
759
                                        else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PATH, server_type, server_address, server_url) == 3 ) { 
 
760
                                                asprintf (&host_name, "%s", server_address);
 
761
                                                use_ssl = server_type_check (server_type);
 
762
                                                server_port = server_port_check (use_ssl);
 
763
                                                check_http ();
 
764
                                        }
 
765
                                        else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
 
766
                                                asprintf (&host_name, "%s", server_address);
 
767
                                                strcpy (server_url, "/");
 
768
                                                use_ssl = server_type_check (server_type);
 
769
                                                server_port = atoi (server_port_text);
 
770
                                                check_http ();
 
771
                                        }
 
772
                                        else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
 
773
                                                asprintf (&host_name, "%s", server_address);
 
774
                                                strcpy (server_url, "/");
 
775
                                                use_ssl = server_type_check (server_type);
 
776
                                                server_port = server_port_check (use_ssl);
 
777
                                                check_http ();
 
778
                                        }
 
779
                                        else if (sscanf (pos, HDR_LOCATION URI_PATH, server_url) == 1) {
 
780
                                                if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
 
781
                                                        *x = '\0';
 
782
                                                        asprintf (&server_url, "%s/%s", orig_url, server_url);
 
783
                                                }
 
784
                                                check_http ();
 
785
                                        }                                       
 
786
                                        pos += (size_t) strcspn (pos, "\r\n");
 
787
                                        pos += (size_t) strspn (pos, "\r\n");
 
788
                                } /* end while (pos) */
 
789
                                printf ("HTTP UNKNOWN: Could not find redirect location - %s%s",
 
790
                                        status_line, (display_html ? "</A>" : ""));
 
791
                                exit (STATE_UNKNOWN);
 
792
                        } /* end if (onredirect == STATE_DEPENDENT) */
 
793
                        
 
794
                        else if (onredirect == STATE_UNKNOWN)
 
795
                                printf ("HTTP UNKNOWN");
 
796
                        else if (onredirect == STATE_OK)
 
797
                                printf ("HTTP ok");
 
798
                        else if (onredirect == STATE_WARNING)
 
799
                                printf ("HTTP WARNING");
 
800
                        else if (onredirect == STATE_CRITICAL)
 
801
                                printf ("HTTP CRITICAL");
 
802
                        elapsed_time = delta_time (tv);
 
803
                        asprintf (&msg, ": %s - %7.3f second response time %s%s|time=%7.3f\n",
 
804
                                 status_line, elapsed_time, timestamp,
 
805
                           (display_html ? "</A>" : ""), elapsed_time);
 
806
                        terminate (onredirect, msg);
 
807
                } /* end if (strstr (status_line, "30[0-4]") */
 
808
 
 
809
 
 
810
        } /* end else (server_expect_yn)  */
 
811
 
 
812
                
 
813
        /* check elapsed time */
 
814
        elapsed_time = delta_time (tv);
 
815
        asprintf (&msg, "HTTP problem: %s - %7.3f second response time %s%s|time=%7.3f\n",
 
816
                       status_line, elapsed_time, timestamp,
 
817
                       (display_html ? "</A>" : ""), elapsed_time);
 
818
        if (check_critical_time == TRUE && elapsed_time > critical_time)
 
819
                terminate (STATE_CRITICAL, msg);
 
820
        if (check_warning_time == TRUE && elapsed_time > warning_time)
 
821
                terminate (STATE_WARNING, msg);
 
822
 
 
823
        /* Page and Header content checks go here */
 
824
        /* these checks should be last */
 
825
 
 
826
        if (strlen (string_expect)) {
 
827
                if (strstr (page, string_expect)) {
 
828
                        printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
 
829
                                status_line, elapsed_time,
 
830
                                timestamp, (display_html ? "</A>" : ""), elapsed_time);
 
831
                        exit (STATE_OK);
 
832
                }
 
833
                else {
 
834
                        printf ("HTTP CRITICAL: string not found%s|time=%7.3f\n",
 
835
                                (display_html ? "</A>" : ""), elapsed_time);
 
836
                        exit (STATE_CRITICAL);
 
837
                }
 
838
        }
 
839
#ifdef HAVE_REGEX_H
 
840
        if (strlen (regexp)) {
 
841
                errcode = regexec (&preg, page, REGS, pmatch, 0);
 
842
                if (errcode == 0) {
 
843
                        printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
 
844
                                status_line, elapsed_time,
 
845
                                timestamp, (display_html ? "</A>" : ""), elapsed_time);
 
846
                        exit (STATE_OK);
 
847
                }
 
848
                else {
 
849
                        if (errcode == REG_NOMATCH) {
 
850
                                printf ("HTTP CRITICAL: pattern not found%s|time=%7.3f\n",
 
851
                                        (display_html ? "</A>" : ""), elapsed_time);
 
852
                                exit (STATE_CRITICAL);
 
853
                        }
 
854
                        else {
 
855
                                regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
 
856
                                printf ("Execute Error: %s\n", errbuf);
 
857
                                exit (STATE_CRITICAL);
 
858
                        }
 
859
                }
 
860
        }
 
861
#endif
 
862
 
 
863
        /* We only get here if all tests have been passed */
 
864
        asprintf (&msg, "HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
 
865
                        status_line, (float)elapsed_time,
 
866
                        timestamp, (display_html ? "</A>" : ""), elapsed_time);
 
867
        terminate (STATE_OK, msg);
 
868
        return STATE_UNKNOWN;
 
869
}
 
870
 
 
871
 
 
872
 
 
873
#ifdef HAVE_SSL
 
874
int connect_SSL (void)
 
875
{
 
876
        SSL_METHOD *meth;
 
877
 
 
878
        asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
 
879
        RAND_seed (randbuff, strlen (randbuff));
 
880
        if (verbose)
 
881
                printf("SSL seeding: %s\n", (RAND_status()==1 ? "OK" : "Failed") );
 
882
 
 
883
        /* Initialize SSL context */
 
884
        SSLeay_add_ssl_algorithms ();
 
885
        meth = SSLv23_client_method ();
 
886
        SSL_load_error_strings ();
 
887
        if ((ctx = SSL_CTX_new (meth)) == NULL) {
 
888
                printf ("ERROR: Cannot create SSL context.\n");
 
889
                return STATE_CRITICAL;
 
890
        }
 
891
 
 
892
        /* Initialize alarm signal handling */
 
893
        signal (SIGALRM, socket_timeout_alarm_handler);
 
894
 
 
895
        /* Set socket timeout */
 
896
        alarm (socket_timeout);
 
897
 
 
898
        /* Save start time */
 
899
        gettimeofday (&tv, NULL);
 
900
 
 
901
        /* Make TCP connection */
 
902
        if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
 
903
                /* Do the SSL handshake */
 
904
                if ((ssl = SSL_new (ctx)) != NULL) {
 
905
                        SSL_set_cipher_list(ssl, "ALL");
 
906
                        SSL_set_fd (ssl, sd);
 
907
                        if (SSL_connect (ssl) != -1)
 
908
                                return OK;
 
909
                        ERR_print_errors_fp (stderr);
 
910
                }
 
911
                else {
 
912
                        printf ("ERROR: Cannot initiate SSL handshake.\n");
 
913
                }
 
914
                SSL_free (ssl);
 
915
        }
 
916
 
 
917
        SSL_CTX_free (ctx);
 
918
        close (sd);
 
919
 
 
920
        return STATE_CRITICAL;
 
921
}
 
922
#endif
 
923
 
 
924
#ifdef HAVE_SSL
 
925
int
 
926
check_certificate (X509 ** certificate)
 
927
{
 
928
        ASN1_STRING *tm;
 
929
        int offset;
 
930
        struct tm stamp;
 
931
        int days_left;
 
932
 
 
933
 
 
934
        /* Retrieve timestamp of certificate */
 
935
        tm = X509_get_notAfter (*certificate);
 
936
 
 
937
        /* Generate tm structure to process timestamp */
 
938
        if (tm->type == V_ASN1_UTCTIME) {
 
939
                if (tm->length < 10) {
 
940
                        printf ("ERROR: Wrong time format in certificate.\n");
 
941
                        return STATE_CRITICAL;
 
942
                }
 
943
                else {
 
944
                        stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
 
945
                        if (stamp.tm_year < 50)
 
946
                                stamp.tm_year += 100;
 
947
                        offset = 0;
 
948
                }
 
949
        }
 
950
        else {
 
951
                if (tm->length < 12) {
 
952
                        printf ("ERROR: Wrong time format in certificate.\n");
 
953
                        return STATE_CRITICAL;
 
954
                }
 
955
                else {
 
956
                        stamp.tm_year =
 
957
                                (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
 
958
                                (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
 
959
                        stamp.tm_year -= 1900;
 
960
                        offset = 2;
 
961
                }
 
962
        }
 
963
        stamp.tm_mon =
 
964
                (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
 
965
        stamp.tm_mday =
 
966
                (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
 
967
        stamp.tm_hour =
 
968
                (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
 
969
        stamp.tm_min =
 
970
                (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
 
971
        stamp.tm_sec = 0;
 
972
        stamp.tm_isdst = -1;
 
973
 
 
974
        days_left = (mktime (&stamp) - time (NULL)) / 86400;
 
975
        snprintf
 
976
                (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
 
977
                 stamp.tm_mon + 1,
 
978
                 stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
 
979
 
 
980
        if (days_left > 0 && days_left <= days_till_exp) {
 
981
                printf ("Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
 
982
                return STATE_WARNING;
 
983
        }
 
984
        if (days_left < 0) {
 
985
                printf ("Certificate expired on %s.\n", timestamp);
 
986
                return STATE_CRITICAL;
 
987
        }
 
988
 
 
989
        if (days_left == 0) {
 
990
                printf ("Certificate expires today (%s).\n", timestamp);
 
991
                return STATE_WARNING;
 
992
        }
 
993
 
 
994
        printf ("Certificate will expire on %s.\n", timestamp);
 
995
 
 
996
        return STATE_OK;
 
997
}
 
998
#endif
 
999
 
 
1000
 
 
1001
 
 
1002
int
 
1003
my_recv (void)
 
1004
{
 
1005
        int i;
 
1006
#ifdef HAVE_SSL
 
1007
        if (use_ssl) {
 
1008
                i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
 
1009
        }
 
1010
        else {
 
1011
                i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
 
1012
        }
 
1013
#else
 
1014
        i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
 
1015
#endif
 
1016
        return i;
 
1017
}
 
1018
 
 
1019
 
 
1020
int
 
1021
my_close (void)
 
1022
{
 
1023
#ifdef HAVE_SSL
 
1024
        if (use_ssl == TRUE) {
 
1025
                SSL_shutdown (ssl);
 
1026
                SSL_free (ssl);
 
1027
                SSL_CTX_free (ctx);
 
1028
                return 0;
 
1029
        }
 
1030
        else {
 
1031
#endif
 
1032
                return close (sd);
 
1033
#ifdef HAVE_SSL
 
1034
        }
 
1035
#endif
 
1036
}
 
1037
 
 
1038
 
 
1039
 
 
1040
void
 
1041
print_help (void)
 
1042
{
 
1043
        print_revision (progname, REVISION);
 
1044
        printf
 
1045
                ("Copyright (c) %s %s <%s>\n\n%s\n",
 
1046
                 COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
 
1047
        print_usage ();
 
1048
        printf ("NOTE: One or both of -H and -I must be specified\n");
 
1049
        printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
 
1050
                DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS, REGOPTIONS);
 
1051
#ifdef HAVE_SSL
 
1052
        printf (SSLDESCRIPTION);
 
1053
#endif
 
1054
}
 
1055
 
 
1056
 
 
1057
void
 
1058
print_usage (void)
 
1059
{
 
1060
        printf ("Usage:\n" " %s %s\n"
 
1061
#ifdef HAVE_GETOPT_H
 
1062
                " %s (-h | --help) for detailed help\n"
 
1063
                " %s (-V | --version) for version information\n",
 
1064
#else
 
1065
                " %s -h for detailed help\n"
 
1066
                " %s -V for version information\n",
 
1067
#endif
 
1068
        progname, OPTIONS, progname, progname);
 
1069
}