aboutsummaryrefslogtreecommitdiff
path: root/plugins/check_curl.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/check_curl.c')
-rw-r--r--plugins/check_curl.c184
1 files changed, 158 insertions, 26 deletions
diff --git a/plugins/check_curl.c b/plugins/check_curl.c
index 2d69b310..14cc8463 100644
--- a/plugins/check_curl.c
+++ b/plugins/check_curl.c
@@ -55,6 +55,12 @@ const char *email = "devel@monitoring-plugins.org";
#include <arpa/inet.h>
+#if defined(HAVE_SSL) && defined(USE_OPENSSL)
+#include <openssl/opensslv.h>
+#endif
+
+#include <netdb.h>
+
#define MAKE_LIBCURL_VERSION(major, minor, patch) ((major)*0x10000 + (minor)*0x100 + (patch))
#define DEFAULT_BUFFER_SIZE 2048
@@ -117,7 +123,7 @@ typedef enum curlhelp_ssl_library {
enum {
REGS = 2,
- MAX_RE_SIZE = 256
+ MAX_RE_SIZE = 1024
};
#include "regex.h"
regex_t preg;
@@ -145,6 +151,7 @@ thresholds *thlds;
char user_agent[DEFAULT_BUFFER_SIZE];
int verbose = 0;
int show_extended_perfdata = FALSE;
+int show_body = FALSE;
int min_page_len = 0;
int max_page_len = 0;
int redir_depth = 0;
@@ -195,6 +202,7 @@ int ssl_version = CURL_SSLVERSION_DEFAULT;
char *client_cert = NULL;
char *client_privkey = NULL;
char *ca_cert = NULL;
+int verify_peer_and_host = FALSE;
int is_openssl_callback = FALSE;
#if defined(HAVE_SSL) && defined(USE_OPENSSL)
X509 *cert = NULL;
@@ -204,6 +212,7 @@ int maximum_age = -1;
int address_family = AF_UNSPEC;
curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN;
int curl_http_version = CURL_HTTP_VERSION_NONE;
+int automatic_decompression = FALSE;
int process_arguments (int, char**);
void handle_curl_option_return_code (CURLcode res, const char* option);
@@ -283,6 +292,20 @@ int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
* TODO: is the last certificate always the server certificate?
*/
cert = X509_STORE_CTX_get_current_cert(x509_ctx);
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ X509_up_ref(cert);
+#endif
+ if (verbose>=2) {
+ puts("* SSL verify callback with certificate:");
+ X509_NAME *subject, *issuer;
+ printf("* issuer:\n");
+ issuer = X509_get_issuer_name( cert );
+ X509_NAME_print_ex_fp(stdout, issuer, 5, XN_FLAG_MULTILINE);
+ printf("* curl verify_callback:\n* subject:\n");
+ subject = X509_get_subject_name( cert );
+ X509_NAME_print_ex_fp(stdout, subject, 5, XN_FLAG_MULTILINE);
+ puts("");
+ }
return 1;
}
@@ -296,6 +319,28 @@ CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm)
#endif /* USE_OPENSSL */
#endif /* HAVE_SSL */
+/* returns a string "HTTP/1.x" or "HTTP/2" */
+static char *string_statuscode (int major, int minor)
+{
+ static char buf[10];
+
+ switch (major) {
+ case 1:
+ snprintf (buf, sizeof (buf), "HTTP/%d.%d", major, minor);
+ break;
+ case 2:
+ case 3:
+ snprintf (buf, sizeof (buf), "HTTP/%d", major);
+ break;
+ default:
+ /* assuming here HTTP/N with N>=4 */
+ snprintf (buf, sizeof (buf), "HTTP/%d", major);
+ break;
+ }
+
+ return buf;
+}
+
/* Checks if the server 'reply' is one of the expected 'statuscodes' */
static int
expected_statuscode (const char *reply, const char *statuscodes)
@@ -327,12 +372,55 @@ handle_curl_option_return_code (CURLcode res, const char* option)
}
int
+lookup_host (const char *host, char *buf, size_t buflen)
+{
+ struct addrinfo hints, *res, *result;
+ int errcode;
+ void *ptr;
+
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_family = address_family;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags |= AI_CANONNAME;
+
+ errcode = getaddrinfo (host, NULL, &hints, &result);
+ if (errcode != 0)
+ return errcode;
+
+ res = result;
+
+ while (res) {
+ inet_ntop (res->ai_family, res->ai_addr->sa_data, buf, buflen);
+ switch (res->ai_family) {
+ case AF_INET:
+ ptr = &((struct sockaddr_in *) res->ai_addr)->sin_addr;
+ break;
+ case AF_INET6:
+ ptr = &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr;
+ break;
+ }
+ inet_ntop (res->ai_family, ptr, buf, buflen);
+ if (verbose >= 1)
+ printf ("* getaddrinfo IPv%d address: %s\n",
+ res->ai_family == PF_INET6 ? 6 : 4, buf);
+ res = res->ai_next;
+ }
+
+ freeaddrinfo(result);
+
+ return 0;
+}
+
+int
check_http (void)
{
int result = STATE_OK;
int page_len = 0;
int i;
char *force_host_header = NULL;
+ struct curl_slist *host = NULL;
+ char addrstr[100];
+ char dnscache[DEFAULT_BUFFER_SIZE];
/* initialize curl */
if (curl_global_init (CURL_GLOBAL_DEFAULT) != CURLE_OK)
@@ -347,6 +435,13 @@ check_http (void)
/* print everything on stdout like check_http would do */
handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_STDERR, stdout), "CURLOPT_STDERR");
+ if (automatic_decompression)
+#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6)
+ handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""), "CURLOPT_ACCEPT_ENCODING");
+#else
+ handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING");
+#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */
+
/* initialize buffer for body of the answer */
if (curlhelp_initwritebuffer(&body_buf) < 0)
die (STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n");
@@ -368,9 +463,12 @@ check_http (void)
// fill dns resolve cache to make curl connect to the given server_address instead of the host_name, only required for ssl, because we use the host_name later on to make SNI happy
if(use_ssl && host_name != NULL) {
- struct curl_slist *host = NULL;
- char dnscache[DEFAULT_BUFFER_SIZE];
- snprintf (dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", host_name, server_port, server_address);
+ if ( (res=lookup_host (server_address, addrstr, 100)) != 0) {
+ snprintf (msg, DEFAULT_BUFFER_SIZE, _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"),
+ server_address, res, gai_strerror (res));
+ die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
+ }
+ snprintf (dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", host_name, server_port, addrstr);
host = curl_slist_append(NULL, dnscache);
curl_easy_setopt(curl, CURLOPT_RESOLVE, host);
if (verbose>=1)
@@ -467,9 +565,11 @@ check_http (void)
if (client_privkey)
handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSLKEY, client_privkey), "CURLOPT_SSLKEY");
if (ca_cert) {
+ handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CAINFO, ca_cert), "CURLOPT_CAINFO");
+ }
+ if (ca_cert || verify_peer_and_host) {
/* per default if we have a CA verify both the peer and the
* hostname in the certificate, can be switched off later */
- handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CAINFO, ca_cert), "CURLOPT_CAINFO");
handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_SSL_VERIFYPEER, 1), "CURLOPT_SSL_VERIFYPEER");
handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_SSL_VERIFYHOST, 2), "CURLOPT_SSL_VERIFYHOST");
} else {
@@ -640,7 +740,7 @@ check_http (void)
/* Curl errors, result in critical Nagios state */
if (res != CURLE_OK) {
snprintf (msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"),
- server_port, res, curl_easy_strerror(res));
+ server_port, res, errbuf[0] ? errbuf : curl_easy_strerror(res));
die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
}
@@ -746,7 +846,8 @@ GOT_FIRST_CERT:
if (curlhelp_parse_statusline (header_buf.buf, &status_line) < 0) {
snprintf (msg, DEFAULT_BUFFER_SIZE, "Unparsable status line in %.3g seconds response time|%s\n",
total_time, perfstring);
- die (STATE_CRITICAL, "HTTP CRITICAL HTTP/1.x %ld unknown - %s", code, msg);
+ /* we cannot know the major/minor version here for sure as we cannot parse the first line */
+ die (STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x %ld unknown - %s", code, msg);
}
/* get result code from cURL */
@@ -766,7 +867,9 @@ GOT_FIRST_CERT:
snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), status_line.first_line);
else
snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), server_port, status_line.first_line);
- die (STATE_CRITICAL, "HTTP CRITICAL - %s", msg);
+ die (STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg,
+ show_body ? "\n" : "",
+ show_body ? body_buf.buf : "");
}
if( server_expect_yn ) {
@@ -823,8 +926,8 @@ GOT_FIRST_CERT:
/* check status codes, set exit status accordingly */
if( status_line.http_code != code ) {
- die (STATE_CRITICAL, _("HTTP CRITICAL HTTP/%d.%d %d %s - different HTTP codes (cUrl has %ld)\n"),
- status_line.http_major, status_line.http_minor,
+ die (STATE_CRITICAL, _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"),
+ string_statuscode (status_line.http_major, status_line.http_minor),
status_line.http_code, status_line.msg, code);
}
@@ -895,13 +998,15 @@ GOT_FIRST_CERT:
msg[strlen(msg)-3] = '\0';
/* TODO: separate _() msg and status code: die (result, "HTTP %s: %s\n", state_text(result), msg); */
- die (result, "HTTP %s: HTTP/%d.%d %d %s%s%s - %d bytes in %.3f second response time %s|%s\n",
- state_text(result), status_line.http_major, status_line.http_minor,
+ die (result, "HTTP %s: %s %d %s%s%s - %d bytes in %.3f second response time %s|%s\n%s%s",
+ state_text(result), string_statuscode (status_line.http_major, status_line.http_minor),
status_line.http_code, status_line.msg,
strlen(msg) > 0 ? " - " : "",
msg, page_len, total_time,
(display_html ? "</A>" : ""),
- perfstring);
+ perfstring,
+ (show_body ? body_buf.buf : ""),
+ (show_body ? "\n" : "") );
/* proper cleanup after die? */
curlhelp_free_statusline(&status_line);
@@ -928,8 +1033,8 @@ char*
uri_string (const UriTextRangeA range, char* buf, size_t buflen)
{
if (!range.first) return "(null)";
- strncpy (buf, range.first, max (buflen, range.afterLast - range.first));
- buf[max (buflen, range.afterLast - range.first)] = '\0';
+ strncpy (buf, range.first, max (buflen-1, range.afterLast - range.first));
+ buf[max (buflen-1, range.afterLast - range.first)] = '\0';
buf[range.afterLast - range.first] = '\0';
return buf;
}
@@ -1041,7 +1146,7 @@ redir (curlhelp_write_curlbuf* header_buf)
const UriPathSegmentA* p = uri.pathHead;
for (; p; p = p->next) {
strncat (new_url, "/", DEFAULT_BUFFER_SIZE);
- strncat (new_url, uri_string (p->text, buf, DEFAULT_BUFFER_SIZE), DEFAULT_BUFFER_SIZE);
+ strncat (new_url, uri_string (p->text, buf, DEFAULT_BUFFER_SIZE), DEFAULT_BUFFER_SIZE-1);
}
}
@@ -1049,8 +1154,8 @@ redir (curlhelp_write_curlbuf* header_buf)
!strncmp(server_address, new_host, MAX_IPV4_HOSTLENGTH) &&
(host_name && !strncmp(host_name, new_host, MAX_IPV4_HOSTLENGTH)) &&
!strcmp(server_url, new_url))
- die (STATE_WARNING,
- _("HTTP WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
+ die (STATE_CRITICAL,
+ _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
use_ssl ? "https" : "http", new_host, new_port, new_url, (display_html ? "</A>" : ""));
/* set new values for redirected request */
@@ -1106,7 +1211,8 @@ process_arguments (int argc, char **argv)
INVERT_REGEX = CHAR_MAX + 1,
SNI_OPTION,
CA_CERT_OPTION,
- HTTP_VERSION_OPTION
+ HTTP_VERSION_OPTION,
+ AUTOMATIC_DECOMPRESSION
};
int option = 0;
@@ -1136,6 +1242,7 @@ process_arguments (int argc, char **argv)
{"client-cert", required_argument, 0, 'J'},
{"private-key", required_argument, 0, 'K'},
{"ca-cert", required_argument, 0, CA_CERT_OPTION},
+ {"verify-cert", no_argument, 0, 'D'},
{"useragent", required_argument, 0, 'A'},
{"header", required_argument, 0, 'k'},
{"no-body", no_argument, 0, 'N'},
@@ -1146,7 +1253,9 @@ process_arguments (int argc, char **argv)
{"use-ipv4", no_argument, 0, '4'},
{"use-ipv6", no_argument, 0, '6'},
{"extended-perfdata", no_argument, 0, 'E'},
+ {"show-body", no_argument, 0, 'B'},
{"http-version", required_argument, 0, HTTP_VERSION_OPTION},
+ {"enable-automatic-decompression", no_argument, 0, AUTOMATIC_DECOMPRESSION},
{0, 0, 0, 0}
};
@@ -1170,7 +1279,7 @@ process_arguments (int argc, char **argv)
server_url = strdup(DEFAULT_SERVER_URL);
while (1) {
- c = getopt_long (argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:nlLS::m:M:NE", longopts, &option);
+ 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:DnlLS::m:M:NEB", longopts, &option);
if (c == -1 || c == EOF || c == 1)
break;
@@ -1310,6 +1419,11 @@ process_arguments (int argc, char **argv)
ca_cert = optarg;
goto enable_ssl;
#endif
+#ifdef LIBCURL_FEATURE_SSL
+ case 'D': /* verify peer certificate & host */
+ verify_peer_and_host = TRUE;
+ break;
+#endif
case 'S': /* use SSL */
#ifdef LIBCURL_FEATURE_SSL
enable_ssl:
@@ -1354,7 +1468,7 @@ process_arguments (int argc, char **argv)
ssl_version = CURL_SSLVERSION_DEFAULT;
#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */
else
- usage4 (_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2 (with optional '+' suffix)"));
+ usage4 (_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 (with optional '+' suffix)"));
}
#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0)
if (got_plus) {
@@ -1513,6 +1627,9 @@ process_arguments (int argc, char **argv)
case 'E': /* show extended perfdata */
show_extended_perfdata = TRUE;
break;
+ case 'B': /* print body content after status line */
+ show_body = TRUE;
+ break;
case HTTP_VERSION_OPTION:
curl_http_version = CURL_HTTP_VERSION_NONE;
if (strcmp (optarg, "1.0") == 0) {
@@ -1530,6 +1647,9 @@ process_arguments (int argc, char **argv)
exit (STATE_WARNING);
}
break;
+ case AUTOMATIC_DECOMPRESSION:
+ automatic_decompression = TRUE;
+ break;
case '?':
/* print short usage statement if args not parsable */
usage5 ();
@@ -1659,7 +1779,7 @@ print_help (void)
printf (" %s\n", "-S, --ssl=VERSION[+]");
printf (" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents"));
printf (" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,"));
- printf (" %s\n", _("1.2 = TLSv1.2). With a '+' suffix, newer versions are also accepted."));
+ printf (" %s\n", _("1.2 = TLSv1.2, 1.3 = TLSv1.3). With a '+' suffix, newer versions are also accepted."));
printf (" %s\n", _("Note: SSLv2 and SSLv3 are deprecated and are usually disabled in libcurl"));
printf (" %s\n", "--sni");
printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
@@ -1680,6 +1800,8 @@ print_help (void)
printf (" %s\n", _("matching the client certificate"));
printf (" %s\n", "--ca-cert=FILE");
printf (" %s\n", _("CA certificate file to verify peer against"));
+ printf (" %s\n", "-D, --verify-cert");
+ printf (" %s\n", _("Verify the peer's SSL certificate and hostname"));
#endif
printf (" %s\n", "-e, --expect=STRING");
@@ -1723,6 +1845,8 @@ print_help (void)
printf (" %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers"));
printf (" %s\n", "-E, --extended-perfdata");
printf (" %s\n", _("Print additional performance data"));
+ printf (" %s\n", "-B, --show-body");
+ printf (" %s\n", _("Print body content below status line"));
printf (" %s\n", "-L, --link");
printf (" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport|curl>");
@@ -1736,6 +1860,8 @@ print_help (void)
printf (" %s\n", "--http-version=VERSION");
printf (" %s\n", _("Connect via specific HTTP protocol."));
printf (" %s\n", _("1.0 = HTTP/1.0, 1.1 = HTTP/1.1, 2.0 = HTTP/2 (HTTP/2 will fail without -S)"));
+ printf (" %s\n", "--enable-automatic-decompression");
+ printf (" %s\n", _("Enable automatic decompression of body (CURLOPT_ACCEPT_ENCODING)."));
printf ("\n");
printf (UT_WARN_CRIT);
@@ -1813,15 +1939,21 @@ print_usage (void)
{
printf ("%s\n", _("Usage:"));
printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname);
- printf (" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate file>]\n");
+ printf (" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate file>] [-D]\n");
printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n");
printf (" [-b proxy_auth] [-f <ok|warning|critcal|follow|sticky|stickyport|curl>]\n");
printf (" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
- printf (" [-A string] [-k string] [-S <version>] [--sni] [-C <warn_age>[,<crit_age>]]\n");
+ printf (" [-A string] [-k string] [-S <version>] [--sni]\n");
printf (" [-T <content-type>] [-j method]\n");
printf (" [--http-version=<version>]\n");
+ printf (" %s -H <vhost> | -I <IP-address> -C <warn_age>[,<crit_age>]\n",progname);
+ printf (" [-p <port>] [-t <timeout>] [-4|-6] [--sni]\n");
printf ("\n");
+#ifdef LIBCURL_FEATURE_SSL
+ printf ("%s\n", _("In the first form, make an HTTP request."));
+ printf ("%s\n\n", _("In the second form, connect to the server and check the TLS certificate."));
+#endif
printf ("%s\n", _("WARNING: check_curl is experimental. Please use"));
printf ("%s\n\n", _("check_http if you need a stable version."));
}
@@ -1944,7 +2076,7 @@ curlhelp_parse_statusline (const char *buf, curlhelp_statusline *status_line)
char *first_line_buf;
/* find last start of a new header */
- start = strrstr2 (buf, "\r\nHTTP");
+ start = strrstr2 (buf, "\r\nHTTP/");
if (start != NULL) {
start += 2;
buf = start;
@@ -2037,7 +2169,7 @@ get_header_value (const struct phr_header* headers, const size_t nof_headers, co
{
int i;
for( i = 0; i < nof_headers; i++ ) {
- if( strncasecmp( header, headers[i].name, max( headers[i].name_len, 4 ) ) == 0 ) {
+ if(headers[i].name != NULL && strncasecmp( header, headers[i].name, max( headers[i].name_len, 4 ) ) == 0 ) {
return strndup( headers[i].value, headers[i].value_len );
}
}