summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--configure.ac15
-rw-r--r--m4/libcurl.m4272
-rw-r--r--plugins/Makefile.am11
-rw-r--r--plugins/check_curl.c1939
-rw-r--r--plugins/picohttpparser.c620
-rw-r--r--plugins/picohttpparser.h89
-rw-r--r--plugins/sslutils.c33
-rw-r--r--plugins/t/check_curl.t204
-rw-r--r--plugins/t/check_http.t68
-rwxr-xr-xplugins/tests/check_curl.t418
-rwxr-xr-xplugins/tests/check_http.t7
-rw-r--r--po/de.po6
13 files changed, 3631 insertions, 53 deletions
diff --git a/.gitignore b/.gitignore
index 3093c6e..5fa4c81 100644
--- a/.gitignore
+++ b/.gitignore
@@ -142,6 +142,7 @@ NP-VERSION-FILE
142/plugins/check_by_ssh 142/plugins/check_by_ssh
143/plugins/check_clamd 143/plugins/check_clamd
144/plugins/check_cluster 144/plugins/check_cluster
145/plugins/check_curl
145/plugins/check_dbi 146/plugins/check_dbi
146/plugins/check_dig 147/plugins/check_dig
147/plugins/check_disk 148/plugins/check_disk
@@ -201,6 +202,7 @@ NP-VERSION-FILE
201/plugins/negate 202/plugins/negate
202/plugins/stamp-h* 203/plugins/stamp-h*
203/plugins/urlize 204/plugins/urlize
205/plugins/libpicohttpparser.a
204 206
205# /plugins/t/ 207# /plugins/t/
206/plugins/t/*.tmp 208/plugins/t/*.tmp
diff --git a/configure.ac b/configure.ac
index bf12995..04ec5bd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -385,6 +385,20 @@ if test "$ac_cv_header_wtsapi32_h" = "yes"; then
385 AC_SUBST(WTSAPI32LIBS) 385 AC_SUBST(WTSAPI32LIBS)
386fi 386fi
387 387
388dnl Check for cURL library
389LIBCURL_CHECK_CONFIG(yes, 7.15.2, [
390 EXTRAS="$EXTRAS check_curl\$(EXEEXT)"
391 LIBCURLINCLUDE="$LIBCURL_CPPFLAGS"
392 LIBCURLLIBS="$LIBCURL"
393 LIBCURLCFLAGS="$LIBCURL_CPPFLAGS"
394 AC_SUBST(LIBCURLINCLUDE)
395 AC_SUBST(LIBCURLLIBS)
396 AC_SUBST(LIBCURLCFLAGS)
397], [
398 AC_MSG_WARN([Skipping curl plugin])
399 AC_MSG_WARN([install libcurl libs to compile this plugin (see REQUIREMENTS).])
400])
401
388dnl Fallback to who(1) if the system doesn't provide an utmpx(5) interface 402dnl Fallback to who(1) if the system doesn't provide an utmpx(5) interface
389if test "$ac_cv_header_utmpx_h" = "no" -a "$ac_cv_header_wtsapi32_h" = "no" 403if test "$ac_cv_header_utmpx_h" = "no" -a "$ac_cv_header_wtsapi32_h" = "no"
390then 404then
@@ -1882,4 +1896,5 @@ ACX_FEATURE([enable],[perl-modules])
1882ACX_FEATURE([with],[cgiurl]) 1896ACX_FEATURE([with],[cgiurl])
1883ACX_FEATURE([with],[trusted-path]) 1897ACX_FEATURE([with],[trusted-path])
1884ACX_FEATURE([enable],[libtap]) 1898ACX_FEATURE([enable],[libtap])
1899ACX_FEATURE([with],[libcurl])
1885 1900
diff --git a/m4/libcurl.m4 b/m4/libcurl.m4
new file mode 100644
index 0000000..53d694d
--- /dev/null
+++ b/m4/libcurl.m4
@@ -0,0 +1,272 @@
1#***************************************************************************
2# _ _ ____ _
3# Project ___| | | | _ \| |
4# / __| | | | |_) | |
5# | (__| |_| | _ <| |___
6# \___|\___/|_| \_\_____|
7#
8# Copyright (C) 2006, David Shaw <dshaw@jabberwocky.com>
9#
10# This software is licensed as described in the file COPYING, which
11# you should have received as part of this distribution. The terms
12# are also available at https://curl.haxx.se/docs/copyright.html.
13#
14# You may opt to use, copy, modify, merge, publish, distribute and/or sell
15# copies of the Software, and permit persons to whom the Software is
16# furnished to do so, under the terms of the COPYING file.
17#
18# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19# KIND, either express or implied.
20#
21###########################################################################
22# LIBCURL_CHECK_CONFIG ([DEFAULT-ACTION], [MINIMUM-VERSION],
23# [ACTION-IF-YES], [ACTION-IF-NO])
24# ----------------------------------------------------------
25# David Shaw <dshaw@jabberwocky.com> May-09-2006
26#
27# Checks for libcurl. DEFAULT-ACTION is the string yes or no to
28# specify whether to default to --with-libcurl or --without-libcurl.
29# If not supplied, DEFAULT-ACTION is yes. MINIMUM-VERSION is the
30# minimum version of libcurl to accept. Pass the version as a regular
31# version number like 7.10.1. If not supplied, any version is
32# accepted. ACTION-IF-YES is a list of shell commands to run if
33# libcurl was successfully found and passed the various tests.
34# ACTION-IF-NO is a list of shell commands that are run otherwise.
35# Note that using --without-libcurl does run ACTION-IF-NO.
36#
37# This macro #defines HAVE_LIBCURL if a working libcurl setup is
38# found, and sets @LIBCURL@ and @LIBCURL_CPPFLAGS@ to the necessary
39# values. Other useful defines are LIBCURL_FEATURE_xxx where xxx are
40# the various features supported by libcurl, and LIBCURL_PROTOCOL_yyy
41# where yyy are the various protocols supported by libcurl. Both xxx
42# and yyy are capitalized. See the list of AH_TEMPLATEs at the top of
43# the macro for the complete list of possible defines. Shell
44# variables $libcurl_feature_xxx and $libcurl_protocol_yyy are also
45# defined to 'yes' for those features and protocols that were found.
46# Note that xxx and yyy keep the same capitalization as in the
47# curl-config list (e.g. it's "HTTP" and not "http").
48#
49# Users may override the detected values by doing something like:
50# LIBCURL="-lcurl" LIBCURL_CPPFLAGS="-I/usr/myinclude" ./configure
51#
52# For the sake of sanity, this macro assumes that any libcurl that is
53# found is after version 7.7.2, the first version that included the
54# curl-config script. Note that it is very important for people
55# packaging binary versions of libcurl to include this script!
56# Without curl-config, we can only guess what protocols are available,
57# or use curl_version_info to figure it out at runtime.
58
59AC_DEFUN([LIBCURL_CHECK_CONFIG],
60[
61 AH_TEMPLATE([LIBCURL_FEATURE_SSL],[Defined if libcurl supports SSL])
62 AH_TEMPLATE([LIBCURL_FEATURE_KRB4],[Defined if libcurl supports KRB4])
63 AH_TEMPLATE([LIBCURL_FEATURE_IPV6],[Defined if libcurl supports IPv6])
64 AH_TEMPLATE([LIBCURL_FEATURE_LIBZ],[Defined if libcurl supports libz])
65 AH_TEMPLATE([LIBCURL_FEATURE_ASYNCHDNS],[Defined if libcurl supports AsynchDNS])
66 AH_TEMPLATE([LIBCURL_FEATURE_IDN],[Defined if libcurl supports IDN])
67 AH_TEMPLATE([LIBCURL_FEATURE_SSPI],[Defined if libcurl supports SSPI])
68 AH_TEMPLATE([LIBCURL_FEATURE_NTLM],[Defined if libcurl supports NTLM])
69
70 AH_TEMPLATE([LIBCURL_PROTOCOL_HTTP],[Defined if libcurl supports HTTP])
71 AH_TEMPLATE([LIBCURL_PROTOCOL_HTTPS],[Defined if libcurl supports HTTPS])
72 AH_TEMPLATE([LIBCURL_PROTOCOL_FTP],[Defined if libcurl supports FTP])
73 AH_TEMPLATE([LIBCURL_PROTOCOL_FTPS],[Defined if libcurl supports FTPS])
74 AH_TEMPLATE([LIBCURL_PROTOCOL_FILE],[Defined if libcurl supports FILE])
75 AH_TEMPLATE([LIBCURL_PROTOCOL_TELNET],[Defined if libcurl supports TELNET])
76 AH_TEMPLATE([LIBCURL_PROTOCOL_LDAP],[Defined if libcurl supports LDAP])
77 AH_TEMPLATE([LIBCURL_PROTOCOL_DICT],[Defined if libcurl supports DICT])
78 AH_TEMPLATE([LIBCURL_PROTOCOL_TFTP],[Defined if libcurl supports TFTP])
79 AH_TEMPLATE([LIBCURL_PROTOCOL_RTSP],[Defined if libcurl supports RTSP])
80 AH_TEMPLATE([LIBCURL_PROTOCOL_POP3],[Defined if libcurl supports POP3])
81 AH_TEMPLATE([LIBCURL_PROTOCOL_IMAP],[Defined if libcurl supports IMAP])
82 AH_TEMPLATE([LIBCURL_PROTOCOL_SMTP],[Defined if libcurl supports SMTP])
83
84 AC_ARG_WITH(libcurl,
85 AS_HELP_STRING([--with-libcurl=PREFIX],[look for the curl library in PREFIX/lib and headers in PREFIX/include]),
86 [_libcurl_with=$withval],[_libcurl_with=ifelse([$1],,[yes],[$1])])
87
88 if test "$_libcurl_with" != "no" ; then
89
90 AC_PROG_AWK
91
92 _libcurl_version_parse="eval $AWK '{split(\$NF,A,\".\"); X=256*256*A[[1]]+256*A[[2]]+A[[3]]; print X;}'"
93
94 _libcurl_try_link=yes
95
96 if test -d "$_libcurl_with" ; then
97 LIBCURL_CPPFLAGS="-I$withval/include"
98 _libcurl_ldflags="-L$withval/lib"
99 AC_PATH_PROG([_libcurl_config],[curl-config],[],
100 ["$withval/bin"])
101 else
102 AC_PATH_PROG([_libcurl_config],[curl-config],[],[$PATH])
103 fi
104
105 if test x$_libcurl_config != "x" ; then
106 AC_CACHE_CHECK([for the version of libcurl],
107 [libcurl_cv_lib_curl_version],
108 [libcurl_cv_lib_curl_version=`$_libcurl_config --version | $AWK '{print $[]2}'`])
109
110 _libcurl_version=`echo $libcurl_cv_lib_curl_version | $_libcurl_version_parse`
111 _libcurl_wanted=`echo ifelse([$2],,[0],[$2]) | $_libcurl_version_parse`
112
113 if test $_libcurl_wanted -gt 0 ; then
114 AC_CACHE_CHECK([for libcurl >= version $2],
115 [libcurl_cv_lib_version_ok],
116 [
117 if test $_libcurl_version -ge $_libcurl_wanted ; then
118 libcurl_cv_lib_version_ok=yes
119 else
120 libcurl_cv_lib_version_ok=no
121 fi
122 ])
123 fi
124
125 if test $_libcurl_wanted -eq 0 || test x$libcurl_cv_lib_version_ok = xyes ; then
126 if test x"$LIBCURL_CPPFLAGS" = "x" ; then
127 LIBCURL_CPPFLAGS=`$_libcurl_config --cflags`
128 fi
129 if test x"$LIBCURL" = "x" ; then
130 LIBCURL=`$_libcurl_config --libs`
131
132 # This is so silly, but Apple actually has a bug in their
133 # curl-config script. Fixed in Tiger, but there are still
134 # lots of Panther installs around.
135 case "${host}" in
136 powerpc-apple-darwin7*)
137 LIBCURL=`echo $LIBCURL | sed -e 's|-arch i386||g'`
138 ;;
139 esac
140 fi
141
142 # All curl-config scripts support --feature
143 _libcurl_features=`$_libcurl_config --feature`
144
145 # Is it modern enough to have --protocols? (7.12.4)
146 if test $_libcurl_version -ge 461828 ; then
147 _libcurl_protocols=`$_libcurl_config --protocols`
148 fi
149 else
150 _libcurl_try_link=no
151 fi
152
153 unset _libcurl_wanted
154 fi
155
156 if test $_libcurl_try_link = yes ; then
157
158 # we didn't find curl-config, so let's see if the user-supplied
159 # link line (or failing that, "-lcurl") is enough.
160 LIBCURL=${LIBCURL-"$_libcurl_ldflags -lcurl"}
161
162 AC_CACHE_CHECK([whether libcurl is usable],
163 [libcurl_cv_lib_curl_usable],
164 [
165 _libcurl_save_cppflags=$CPPFLAGS
166 CPPFLAGS="$LIBCURL_CPPFLAGS $CPPFLAGS"
167 _libcurl_save_libs=$LIBS
168 LIBS="$LIBCURL $LIBS"
169
170 AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <curl/curl.h>]],[[
171/* Try and use a few common options to force a failure if we are
172 missing symbols or can't link. */
173int x;
174curl_easy_setopt(NULL,CURLOPT_URL,NULL);
175x=CURL_ERROR_SIZE;
176x=CURLOPT_WRITEFUNCTION;
177x=CURLOPT_WRITEDATA;
178x=CURLOPT_ERRORBUFFER;
179x=CURLOPT_STDERR;
180x=CURLOPT_VERBOSE;
181if (x) {;}
182]])],libcurl_cv_lib_curl_usable=yes,libcurl_cv_lib_curl_usable=no)
183
184 CPPFLAGS=$_libcurl_save_cppflags
185 LIBS=$_libcurl_save_libs
186 unset _libcurl_save_cppflags
187 unset _libcurl_save_libs
188 ])
189
190 if test $libcurl_cv_lib_curl_usable = yes ; then
191
192 # Does curl_free() exist in this version of libcurl?
193 # If not, fake it with free()
194
195 _libcurl_save_cppflags=$CPPFLAGS
196 CPPFLAGS="$CPPFLAGS $LIBCURL_CPPFLAGS"
197 _libcurl_save_libs=$LIBS
198 LIBS="$LIBS $LIBCURL"
199
200 AC_CHECK_FUNC(curl_free,,
201 AC_DEFINE(curl_free,free,
202 [Define curl_free() as free() if our version of curl lacks curl_free.]))
203
204 CPPFLAGS=$_libcurl_save_cppflags
205 LIBS=$_libcurl_save_libs
206 unset _libcurl_save_cppflags
207 unset _libcurl_save_libs
208
209 AC_DEFINE(HAVE_LIBCURL,1,
210 [Define to 1 if you have a functional curl library.])
211 AC_SUBST(LIBCURL_CPPFLAGS)
212 AC_SUBST(LIBCURL)
213
214 for _libcurl_feature in $_libcurl_features ; do
215 AC_DEFINE_UNQUOTED(AS_TR_CPP(libcurl_feature_$_libcurl_feature),[1])
216 eval AS_TR_SH(libcurl_feature_$_libcurl_feature)=yes
217 done
218
219 if test "x$_libcurl_protocols" = "x" ; then
220
221 # We don't have --protocols, so just assume that all
222 # protocols are available
223 _libcurl_protocols="HTTP FTP FILE TELNET LDAP DICT TFTP"
224
225 if test x$libcurl_feature_SSL = xyes ; then
226 _libcurl_protocols="$_libcurl_protocols HTTPS"
227
228 # FTPS wasn't standards-compliant until version
229 # 7.11.0 (0x070b00 == 461568)
230 if test $_libcurl_version -ge 461568; then
231 _libcurl_protocols="$_libcurl_protocols FTPS"
232 fi
233 fi
234
235 # RTSP, IMAP, POP3 and SMTP were added in
236 # 7.20.0 (0x071400 == 463872)
237 if test $_libcurl_version -ge 463872; then
238 _libcurl_protocols="$_libcurl_protocols RTSP IMAP POP3 SMTP"
239 fi
240 fi
241
242 for _libcurl_protocol in $_libcurl_protocols ; do
243 AC_DEFINE_UNQUOTED(AS_TR_CPP(libcurl_protocol_$_libcurl_protocol),[1])
244 eval AS_TR_SH(libcurl_protocol_$_libcurl_protocol)=yes
245 done
246 else
247 unset LIBCURL
248 unset LIBCURL_CPPFLAGS
249 fi
250 fi
251
252 unset _libcurl_try_link
253 unset _libcurl_version_parse
254 unset _libcurl_config
255 unset _libcurl_feature
256 unset _libcurl_features
257 unset _libcurl_protocol
258 unset _libcurl_protocols
259 unset _libcurl_version
260 unset _libcurl_ldflags
261 fi
262
263 if test x$_libcurl_with = xno || test x$libcurl_cv_lib_curl_usable != xyes ; then
264 # This is the IF-NO path
265 ifelse([$4],,:,[$4])
266 else
267 # This is the IF-YES path
268 ifelse([$3],,:,[$3])
269 fi
270
271 unset _libcurl_with
272])dnl
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 0ddf9bd..ff745c3 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -38,17 +38,19 @@ check_tcp_programs = check_ftp check_imap check_nntp check_pop \
38EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_snmp check_hpjd \ 38EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_snmp check_hpjd \
39 check_swap check_fping check_ldap check_game check_dig \ 39 check_swap check_fping check_ldap check_game check_dig \
40 check_nagios check_by_ssh check_dns check_nt check_ide_smart \ 40 check_nagios check_by_ssh check_dns check_nt check_ide_smart \
41 check_procs check_mysql_query check_apt check_dbi 41 check_procs check_mysql_query check_apt check_dbi check_curl
42 42
43EXTRA_DIST = t tests 43EXTRA_DIST = t tests
44 44
45PLUGINHDRS = common.h 45PLUGINHDRS = common.h
46 46
47noinst_LIBRARIES = libnpcommon.a 47noinst_LIBRARIES = libnpcommon.a libpicohttpparser.a
48 48
49libnpcommon_a_SOURCES = utils.c netutils.c sslutils.c runcmd.c \ 49libnpcommon_a_SOURCES = utils.c netutils.c sslutils.c runcmd.c \
50 popen.c utils.h netutils.h popen.h common.h runcmd.c runcmd.h 50 popen.c utils.h netutils.h popen.h common.h runcmd.c runcmd.h
51 51
52libpicohttpparser_a_SOURCES = picohttpparser.c
53
52BASEOBJS = libnpcommon.a ../lib/libmonitoringplug.a ../gl/libgnu.a 54BASEOBJS = libnpcommon.a ../lib/libmonitoringplug.a ../gl/libgnu.a
53NETOBJS = $(BASEOBJS) $(EXTRA_NETOBLS) 55NETOBJS = $(BASEOBJS) $(EXTRA_NETOBLS)
54NETLIBS = $(NETOBJS) $(SOCKETLIBS) 56NETLIBS = $(NETOBJS) $(SOCKETLIBS)
@@ -69,6 +71,9 @@ test-debug:
69 71
70check_apt_LDADD = $(BASEOBJS) 72check_apt_LDADD = $(BASEOBJS)
71check_cluster_LDADD = $(BASEOBJS) 73check_cluster_LDADD = $(BASEOBJS)
74check_curl_CFLAGS = $(AM_CFLAGS) $(LIBCURLCFLAGS)
75check_curl_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCURLINCLUDE)
76check_curl_LDADD = $(NETLIBS) $(LIBCURLLIBS) $(SSLOBJS) libpicohttpparser.a
72check_dbi_LDADD = $(NETLIBS) $(DBILIBS) 77check_dbi_LDADD = $(NETLIBS) $(DBILIBS)
73check_dig_LDADD = $(NETLIBS) 78check_dig_LDADD = $(NETLIBS)
74check_disk_LDADD = $(BASEOBJS) 79check_disk_LDADD = $(BASEOBJS)
@@ -89,7 +94,7 @@ check_mysql_query_CFLAGS = $(AM_CFLAGS) $(MYSQLCFLAGS)
89check_mysql_query_CPPFLAGS = $(AM_CPPFLAGS) $(MYSQLINCLUDE) 94check_mysql_query_CPPFLAGS = $(AM_CPPFLAGS) $(MYSQLINCLUDE)
90check_mysql_query_LDADD = $(NETLIBS) $(MYSQLLIBS) 95check_mysql_query_LDADD = $(NETLIBS) $(MYSQLLIBS)
91check_nagios_LDADD = $(BASEOBJS) 96check_nagios_LDADD = $(BASEOBJS)
92check_nt_LDADD = $(NETLIBS) 97check_nt_LDADD = $(NETLIBS)
93check_ntp_LDADD = $(NETLIBS) $(MATHLIBS) 98check_ntp_LDADD = $(NETLIBS) $(MATHLIBS)
94check_ntp_peer_LDADD = $(NETLIBS) $(MATHLIBS) 99check_ntp_peer_LDADD = $(NETLIBS) $(MATHLIBS)
95check_nwstat_LDADD = $(NETLIBS) 100check_nwstat_LDADD = $(NETLIBS)
diff --git a/plugins/check_curl.c b/plugins/check_curl.c
new file mode 100644
index 0000000..5f6905f
--- /dev/null
+++ b/plugins/check_curl.c
@@ -0,0 +1,1939 @@
1/*****************************************************************************
2*
3* Monitoring check_curl plugin
4*
5* License: GPL
6* Copyright (c) 1999-2017 Monitoring Plugins Development Team
7*
8* Description:
9*
10* This file contains the check_curl 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* This plugin uses functions from the curl library, see
18* http://curl.haxx.se
19*
20* This program is free software: you can redistribute it and/or modify
21* it under the terms of the GNU General Public License as published by
22* the Free Software Foundation, either version 3 of the License, or
23* (at your option) any later version.
24*
25* This program is distributed in the hope that it will be useful,
26* but WITHOUT ANY WARRANTY; without even the implied warranty of
27* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28* GNU General Public License for more details.
29*
30* You should have received a copy of the GNU General Public License
31* along with this program. If not, see <http://www.gnu.org/licenses/>.
32*
33*
34*****************************************************************************/
35const char *progname = "check_curl";
36
37const char *copyright = "2006-2017";
38const char *email = "devel@monitoring-plugins.org";
39
40#include <ctype.h>
41
42#include "common.h"
43#include "utils.h"
44
45#ifndef LIBCURL_PROTOCOL_HTTP
46#error libcurl compiled without HTTP support, compiling check_curl plugin does not makes a lot of sense
47#endif
48
49#include "curl/curl.h"
50#include "curl/easy.h"
51
52#include "picohttpparser.h"
53
54#define MAKE_LIBCURL_VERSION(major, minor, patch) ((major)*0x10000 + (minor)*0x100 + (patch))
55
56#define DEFAULT_BUFFER_SIZE 2048
57#define DEFAULT_SERVER_URL "/"
58#define HTTP_EXPECT "HTTP/1."
59enum {
60 HTTP_PORT = 80,
61 HTTPS_PORT = 443,
62 MAX_PORT = 65535
63};
64
65/* for buffers for header and body */
66typedef struct {
67 char *buf;
68 size_t buflen;
69 size_t bufsize;
70} curlhelp_write_curlbuf;
71
72/* for buffering the data sent in PUT */
73typedef struct {
74 char *buf;
75 size_t buflen;
76 off_t pos;
77} curlhelp_read_curlbuf;
78
79/* for parsing the HTTP status line */
80typedef struct {
81 int http_major; /* major version of the protocol, always 1 (HTTP/0.9
82 * never reached the big internet most likely) */
83 int http_minor; /* minor version of the protocol, usually 0 or 1 */
84 int http_code; /* HTTP return code as in RFC 2145 */
85 int http_subcode; /* Microsoft IIS extension, HTTP subcodes, see
86 * http://support.microsoft.com/kb/318380/en-us */
87 const char *msg; /* the human readable message */
88 char *first_line; /* a copy of the first line */
89} curlhelp_statusline;
90
91/* to know the underlying SSL library used by libcurl */
92typedef enum curlhelp_ssl_library {
93 CURLHELP_SSL_LIBRARY_UNKNOWN,
94 CURLHELP_SSL_LIBRARY_OPENSSL,
95 CURLHELP_SSL_LIBRARY_LIBRESSL,
96 CURLHELP_SSL_LIBRARY_GNUTLS,
97 CURLHELP_SSL_LIBRARY_NSS
98} curlhelp_ssl_library;
99
100enum {
101 REGS = 2,
102 MAX_RE_SIZE = 256
103};
104#include "regex.h"
105regex_t preg;
106regmatch_t pmatch[REGS];
107char regexp[MAX_RE_SIZE];
108int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
109int errcode;
110int invert_regex = 0;
111
112char *server_address;
113char *host_name;
114char *server_url = DEFAULT_SERVER_URL;
115char server_ip[DEFAULT_BUFFER_SIZE];
116struct curl_slist *server_ips = NULL;
117unsigned short server_port = HTTP_PORT;
118int virtual_port = 0;
119int host_name_length;
120char output_header_search[30] = "";
121char output_string_search[30] = "";
122char *warning_thresholds = NULL;
123char *critical_thresholds = NULL;
124int days_till_exp_warn, days_till_exp_crit;
125thresholds *thlds;
126char user_agent[DEFAULT_BUFFER_SIZE];
127int verbose = 0;
128int show_extended_perfdata = FALSE;
129int min_page_len = 0;
130int max_page_len = 0;
131char *http_method = NULL;
132char *http_post_data = NULL;
133char *http_content_type = NULL;
134CURL *curl;
135struct curl_slist *header_list = NULL;
136curlhelp_write_curlbuf body_buf;
137curlhelp_write_curlbuf header_buf;
138curlhelp_statusline status_line;
139curlhelp_read_curlbuf put_buf;
140char http_header[DEFAULT_BUFFER_SIZE];
141long code;
142long socket_timeout = DEFAULT_SOCKET_TIMEOUT;
143double total_time;
144double time_connect;
145double time_appconnect;
146double time_headers;
147double time_firstbyte;
148char errbuf[CURL_ERROR_SIZE+1];
149CURLcode res;
150char url[DEFAULT_BUFFER_SIZE];
151char msg[DEFAULT_BUFFER_SIZE];
152char perfstring[DEFAULT_BUFFER_SIZE];
153char header_expect[MAX_INPUT_BUFFER] = "";
154char string_expect[MAX_INPUT_BUFFER] = "";
155char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
156int server_expect_yn = 0;
157char user_auth[MAX_INPUT_BUFFER] = "";
158int display_html = FALSE;
159int onredirect = STATE_OK;
160int use_ssl = FALSE;
161int use_sni = TRUE;
162int check_cert = FALSE;
163typedef union {
164 struct curl_slist* to_info;
165 struct curl_certinfo* to_certinfo;
166} cert_ptr_union;
167cert_ptr_union cert_ptr;
168int ssl_version = CURL_SSLVERSION_DEFAULT;
169char *client_cert = NULL;
170char *client_privkey = NULL;
171char *ca_cert = NULL;
172int is_openssl_callback = FALSE;
173#if defined(HAVE_SSL) && defined(USE_OPENSSL)
174X509 *cert = NULL;
175#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */
176int no_body = FALSE;
177int maximum_age = -1;
178int address_family = AF_UNSPEC;
179curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN;
180
181int process_arguments (int, char**);
182void handle_curl_option_return_code (CURLcode res, const char* option);
183int check_http (void);
184void print_help (void);
185void print_usage (void);
186void print_curl_version (void);
187int curlhelp_initwritebuffer (curlhelp_write_curlbuf*);
188int curlhelp_buffer_write_callback (void*, size_t , size_t , void*);
189void curlhelp_freewritebuffer (curlhelp_write_curlbuf*);
190int curlhelp_initreadbuffer (curlhelp_read_curlbuf *, const char *, size_t);
191int curlhelp_buffer_read_callback (void *, size_t , size_t , void *);
192void curlhelp_freereadbuffer (curlhelp_read_curlbuf *);
193curlhelp_ssl_library curlhelp_get_ssl_library (CURL*);
194const char* curlhelp_get_ssl_library_string (curlhelp_ssl_library);
195int net_noopenssl_check_certificate (cert_ptr_union*, int, int);
196
197int curlhelp_parse_statusline (const char*, curlhelp_statusline *);
198void curlhelp_free_statusline (curlhelp_statusline *);
199char *perfd_time_ssl (double microsec);
200char *get_header_value (const struct phr_header* headers, const size_t nof_headers, const char* header);
201int check_document_dates (const curlhelp_write_curlbuf *, char (*msg)[DEFAULT_BUFFER_SIZE]);
202int get_content_length (const curlhelp_write_curlbuf* header_buf, const curlhelp_write_curlbuf* body_buf);
203
204#if defined(HAVE_SSL) && defined(USE_OPENSSL)
205int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit);
206#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */
207
208void remove_newlines (char *);
209void test_file (char *);
210
211int
212main (int argc, char **argv)
213{
214 int result = STATE_UNKNOWN;
215
216 setlocale (LC_ALL, "");
217 bindtextdomain (PACKAGE, LOCALEDIR);
218 textdomain (PACKAGE);
219
220 /* Parse extra opts if any */
221 argv = np_extra_opts (&argc, argv, progname);
222
223 /* set defaults */
224 snprintf( user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s)",
225 progname, NP_VERSION, VERSION);
226
227 /* parse arguments */
228 if (process_arguments (argc, argv) == ERROR)
229 usage4 (_("Could not parse arguments"));
230
231 if (display_html == TRUE)
232 printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">",
233 use_ssl ? "https" : "http", host_name ? host_name : server_address,
234 server_port, server_url);
235
236 result = check_http ();
237 return result;
238}
239
240#ifdef HAVE_SSL
241#ifdef USE_OPENSSL
242
243int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
244{
245 /* TODO: we get all certificates of the chain, so which ones
246 * should we test?
247 * TODO: is the last certificate always the server certificate?
248 */
249 cert = X509_STORE_CTX_get_current_cert(x509_ctx);
250 return 1;
251}
252
253CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm)
254{
255 SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER, verify_callback);
256
257 return CURLE_OK;
258}
259
260#endif /* USE_OPENSSL */
261#endif /* HAVE_SSL */
262
263/* Checks if the server 'reply' is one of the expected 'statuscodes' */
264static int
265expected_statuscode (const char *reply, const char *statuscodes)
266{
267 char *expected, *code;
268 int result = 0;
269
270 if ((expected = strdup (statuscodes)) == NULL)
271 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
272
273 for (code = strtok (expected, ","); code != NULL; code = strtok (NULL, ","))
274 if (strstr (reply, code) != NULL) {
275 result = 1;
276 break;
277 }
278
279 free (expected);
280 return result;
281}
282
283void
284handle_curl_option_return_code (CURLcode res, const char* option)
285{
286 if (res != CURLE_OK) {
287 snprintf (msg, DEFAULT_BUFFER_SIZE, _("Error while setting cURL option '%s': cURL returned %d - %s"),
288 option, res, curl_easy_strerror(res));
289 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
290 }
291}
292
293int
294check_http (void)
295{
296 int result = STATE_OK;
297 int page_len = 0;
298
299 /* initialize curl */
300 if (curl_global_init (CURL_GLOBAL_DEFAULT) != CURLE_OK)
301 die (STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n");
302
303 if ((curl = curl_easy_init()) == NULL)
304 die (STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n");
305
306 if (verbose >= 1)
307 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_VERBOSE, TRUE), "CURLOPT_VERBOSE");
308
309 /* print everything on stdout like check_http would do */
310 handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_STDERR, stdout), "CURLOPT_STDERR");
311
312 /* initialize buffer for body of the answer */
313 if (curlhelp_initwritebuffer(&body_buf) < 0)
314 die (STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n");
315 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback), "CURLOPT_WRITEFUNCTION");
316 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_WRITEDATA, (void *)&body_buf), "CURLOPT_WRITEDATA");
317
318 /* initialize buffer for header of the answer */
319 if (curlhelp_initwritebuffer( &header_buf ) < 0)
320 die (STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n" );
321 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback), "CURLOPT_HEADERFUNCTION");
322 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_WRITEHEADER, (void *)&header_buf), "CURLOPT_WRITEHEADER");
323
324 /* set the error buffer */
325 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, errbuf), "CURLOPT_ERRORBUFFER");
326
327 /* set timeouts */
328 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, socket_timeout), "CURLOPT_CONNECTTIMEOUT");
329 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_TIMEOUT, socket_timeout), "CURLOPT_TIMEOUT");
330
331 /* compose URL: must be the host_name, only if not given take the IP address. */
332 snprintf (url, DEFAULT_BUFFER_SIZE, "%s://%s%s", use_ssl ? "https" : "http",
333 host_name ? host_name : server_address, server_url);
334 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_URL, url), "CURLOPT_URL");
335
336 /* cURL does certificate checking with this host_name (and not the virtual host?
337 * So we force CURLOPT_RESOLVE to make sure the resolver pickes the right IP
338 * for this hostname. */
339#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 3)
340 if (host_name && strcmp (host_name, server_address)) {
341 snprintf (server_ip, DEFAULT_BUFFER_SIZE, "%s:%d:%s", host_name, server_port, server_address);
342 server_ips = curl_slist_append (server_ips, server_ip);
343 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_RESOLVE, server_ips), "CURLOPT_RESOLVE");
344 }
345#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 3) */
346
347 /* set port */
348 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_PORT, server_port), "CURLOPT_PORT");
349
350 /* set HTTP method */
351 if (http_method) {
352 if (!strcmp(http_method, "POST"))
353 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_POST, 1), "CURLOPT_POST");
354 else if (!strcmp(http_method, "PUT"))
355 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD");
356 else
357 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CUSTOMREQUEST, http_method), "CURLOPT_CUSTOMREQUEST");
358 }
359
360 /* set hostname (virtual hosts) */
361 if(host_name != NULL) {
362 if((virtual_port != HTTP_PORT && !use_ssl) || (virtual_port != HTTPS_PORT && use_ssl)) {
363 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", host_name, virtual_port);
364 } else {
365 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", host_name);
366 }
367 header_list = curl_slist_append (header_list, http_header);
368 }
369
370 /* always close connection, be nice to servers */
371 snprintf (http_header, DEFAULT_BUFFER_SIZE, "Connection: close");
372 header_list = curl_slist_append (header_list, http_header);
373
374 /* set HTTP headers */
375 handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPHEADER, header_list ), "CURLOPT_HTTPHEADER");
376
377#ifdef LIBCURL_FEATURE_SSL
378
379 /* set SSL version, warn about unsecure or unsupported versions */
380 if (use_ssl) {
381 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSLVERSION, ssl_version), "CURLOPT_SSLVERSION");
382 }
383
384 /* client certificate and key to present to server (SSL) */
385 if (client_cert)
386 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSLCERT, client_cert), "CURLOPT_SSLCERT");
387 if (client_privkey)
388 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSLKEY, client_privkey), "CURLOPT_SSLKEY");
389 if (ca_cert) {
390 /* per default if we have a CA verify both the peer and the
391 * hostname in the certificate, can be switched off later */
392 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CAINFO, ca_cert), "CURLOPT_CAINFO");
393 handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_SSL_VERIFYPEER, 1), "CURLOPT_SSL_VERIFYPEER");
394 handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_SSL_VERIFYHOST, 2), "CURLOPT_SSL_VERIFYHOST");
395 } else {
396 /* backward-compatible behaviour, be tolerant in checks
397 * TODO: depending on more options have aspects we want
398 * to be less tolerant about ssl verfications
399 */
400 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0), "CURLOPT_SSL_VERIFYPEER");
401 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0), "CURLOPT_SSL_VERIFYHOST");
402 }
403
404 /* detect SSL library used by libcurl */
405 ssl_library = curlhelp_get_ssl_library (curl);
406
407 /* try hard to get a stack of certificates to verify against */
408 if (check_cert)
409#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1)
410 /* inform curl to report back certificates */
411 switch (ssl_library) {
412 case CURLHELP_SSL_LIBRARY_OPENSSL:
413 case CURLHELP_SSL_LIBRARY_LIBRESSL:
414 /* set callback to extract certificate with OpenSSL context function (works with
415 * OpenSSL-style libraries only!) */
416#ifdef USE_OPENSSL
417 /* libcurl and monitoring plugins built with OpenSSL, good */
418 handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION");
419 is_openssl_callback = TRUE;
420#else /* USE_OPENSSL */
421#endif /* USE_OPENSSL */
422 /* libcurl is built with OpenSSL, monitoring plugins, so falling
423 * back to manually extracting certificate information */
424 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
425 break;
426
427 case CURLHELP_SSL_LIBRARY_NSS:
428#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
429 /* NSS: support for CERTINFO is implemented since 7.34.0 */
430 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
431#else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
432 die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library '%s' is too old)\n", curlhelp_get_ssl_library_string (ssl_library));
433#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
434 break;
435
436 case CURLHELP_SSL_LIBRARY_GNUTLS:
437#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0)
438 /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */
439 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
440#else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
441 die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library '%s' is too old)\n", curlhelp_get_ssl_library_string (ssl_library));
442#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
443 break;
444
445 case CURLHELP_SSL_LIBRARY_UNKNOWN:
446 default:
447 die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (unknown SSL library '%s', must implement first)\n", curlhelp_get_ssl_library_string (ssl_library));
448 break;
449 }
450#else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
451 /* old libcurl, our only hope is OpenSSL, otherwise we are out of luck */
452 if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL || ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL)
453 handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION");
454 else
455 die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl too old and has no CURLOPT_CERTINFO)\n");
456#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
457
458#endif /* LIBCURL_FEATURE_SSL */
459
460 /* set default or user-given user agent identification */
461 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_USERAGENT, user_agent), "CURLOPT_USERAGENT");
462
463 /* authentication */
464 if (strcmp(user_auth, ""))
465 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_USERPWD, user_auth), "CURLOPT_USERPWD");
466
467 /* TODO: parameter auth method, bitfield of following methods:
468 * CURLAUTH_BASIC (default)
469 * CURLAUTH_DIGEST
470 * CURLAUTH_DIGEST_IE
471 * CURLAUTH_NEGOTIATE
472 * CURLAUTH_NTLM
473 * CURLAUTH_NTLM_WB
474 *
475 * convenience tokens for typical sets of methods:
476 * CURLAUTH_ANYSAFE: most secure, without BASIC
477 * or CURLAUTH_ANY: most secure, even BASIC if necessary
478 *
479 * handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_DIGEST ), "CURLOPT_HTTPAUTH");
480 */
481
482 /* handle redirections */
483 if (onredirect == STATE_DEPENDENT) {
484 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1), "CURLOPT_FOLLOWLOCATION");
485 /* TODO: handle the following aspects of redirection
486 CURLOPT_POSTREDIR: method switch
487 CURLINFO_REDIRECT_URL: custom redirect option
488 CURLOPT_REDIRECT_PROTOCOLS
489 CURLINFO_REDIRECT_COUNT
490 */
491 }
492
493 /* no-body */
494 if (no_body)
495 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_NOBODY, 1), "CURLOPT_NOBODY");
496
497 /* IPv4 or IPv6 forced DNS resolution */
498 if (address_family == AF_UNSPEC)
499 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)");
500 else if (address_family == AF_INET)
501 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)");
502#if defined (USE_IPV6) && defined (LIBCURL_FEATURE_IPV6)
503 else if (address_family == AF_INET6)
504 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)");
505#endif
506
507 /* either send http POST data (any data, not only POST)*/
508 if (!strcmp(http_method, "POST") ||!strcmp(http_method, "PUT")) {
509 /* set content of payload for POST and PUT */
510 if (http_content_type) {
511 snprintf (http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", http_content_type);
512 header_list = curl_slist_append (header_list, http_header);
513 }
514 /* NULL indicates "HTTP Continue" in libcurl, provide an empty string
515 * in case of no POST/PUT data */
516 if (!http_post_data)
517 http_post_data = "";
518 if (!strcmp(http_method, "POST")) {
519 /* POST method, set payload with CURLOPT_POSTFIELDS */
520 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_POSTFIELDS, http_post_data), "CURLOPT_POSTFIELDS");
521 } else if (!strcmp(http_method, "PUT")) {
522 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_READFUNCTION, (curl_read_callback)curlhelp_buffer_read_callback), "CURLOPT_READFUNCTION");
523 curlhelp_initreadbuffer (&put_buf, http_post_data, strlen (http_post_data));
524 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_READDATA, (void *)&put_buf), "CURLOPT_READDATA");
525 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_INFILESIZE, (curl_off_t)strlen (http_post_data)), "CURLOPT_INFILESIZE");
526 }
527 }
528
529 /* do the request */
530 res = curl_easy_perform(curl);
531
532 if (verbose>=2 && http_post_data)
533 printf ("**** REQUEST CONTENT ****\n%s\n", http_post_data);
534
535 /* free header and server IP resolve lists, we don't need it anymore */
536 curl_slist_free_all (header_list);
537 curl_slist_free_all (server_ips);
538
539 /* Curl errors, result in critical Nagios state */
540 if (res != CURLE_OK) {
541 snprintf (msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"),
542 server_port, res, curl_easy_strerror(res));
543 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
544 }
545
546 /* certificate checks */
547#ifdef LIBCURL_FEATURE_SSL
548 if (use_ssl == TRUE) {
549 if (check_cert == TRUE) {
550 if (is_openssl_callback) {
551#ifdef USE_OPENSSL
552 /* check certificate with OpenSSL functions, curl has been built against OpenSSL
553 * and we actually have OpenSSL in the monitoring tools
554 */
555 result = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit);
556 return result;
557#else /* USE_OPENSSL */
558 die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL callback used and not linked against OpenSSL\n");
559#endif /* USE_OPENSSL */
560 } else {
561 int i;
562 struct curl_slist *slist;
563
564 cert_ptr.to_info = NULL;
565 res = curl_easy_getinfo (curl, CURLINFO_CERTINFO, &cert_ptr.to_info);
566 if (!res && cert_ptr.to_info) {
567#ifdef USE_OPENSSL
568 /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert parsing
569 * We only check the first certificate and assume it's the one of the server
570 */
571 const char* raw_cert = NULL;
572 for (i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) {
573 for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) {
574 if (verbose >= 2)
575 printf ("%d ** %s\n", i, slist->data);
576 if (strncmp (slist->data, "Cert:", 5) == 0) {
577 raw_cert = &slist->data[5];
578 goto GOT_FIRST_CERT;
579 }
580 }
581 }
582GOT_FIRST_CERT:
583 if (!raw_cert) {
584 snprintf (msg, DEFAULT_BUFFER_SIZE, _("Cannot retrieve certificates from CERTINFO information - certificate data was empty"));
585 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
586 }
587 BIO* cert_BIO = BIO_new (BIO_s_mem());
588 BIO_write (cert_BIO, raw_cert, strlen(raw_cert));
589 cert = PEM_read_bio_X509 (cert_BIO, NULL, NULL, NULL);
590 if (!cert) {
591 snprintf (msg, DEFAULT_BUFFER_SIZE, _("Cannot read certificate from CERTINFO information - BIO error"));
592 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
593 }
594 BIO_free (cert_BIO);
595 result = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit);
596 return result;
597#else /* USE_OPENSSL */
598 /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our disposal,
599 * so we use the libcurl CURLINFO data
600 */
601 result = net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn, days_till_exp_crit);
602 return result;
603#endif /* USE_OPENSSL */
604 } else {
605 snprintf (msg, DEFAULT_BUFFER_SIZE, _("Cannot retrieve certificates - cURL returned %d - %s"),
606 res, curl_easy_strerror(res));
607 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
608 }
609 }
610 }
611 }
612#endif /* LIBCURL_FEATURE_SSL */
613
614 /* we got the data and we executed the request in a given time, so we can append
615 * performance data to the answer always
616 */
617 handle_curl_option_return_code (curl_easy_getinfo (curl, CURLINFO_TOTAL_TIME, &total_time), "CURLINFO_TOTAL_TIME");
618 if(show_extended_perfdata) {
619 handle_curl_option_return_code (curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &time_connect), "CURLINFO_CONNECT_TIME");
620 handle_curl_option_return_code (curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), "CURLINFO_APPCONNECT_TIME");
621 handle_curl_option_return_code (curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, &time_headers), "CURLINFO_PRETRANSFER_TIME");
622 handle_curl_option_return_code (curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), "CURLINFO_STARTTRANSFER_TIME");
623 snprintf(perfstring, DEFAULT_BUFFER_SIZE, "time=%.6gs;%.6g;%.6g;; size=%dB;;; time_connect=%.6gs;;;; %s time_headers=%.6gs;;;; time_firstbyte=%.6gs;;;; time_transfer=%.6gs;;;;",
624 total_time,
625 warning_thresholds != NULL ? (double)thlds->warning->end : 0.0,
626 critical_thresholds != NULL ? (double)thlds->critical->end : 0.0,
627 (int)body_buf.buflen,
628 time_connect,
629 use_ssl == TRUE ? perfd_time_ssl(time_appconnect-time_connect) : "",
630 (time_headers - time_appconnect),
631 (time_firstbyte - time_headers),
632 (total_time-time_firstbyte)
633 );
634 } else {
635 snprintf(perfstring, DEFAULT_BUFFER_SIZE, "time=%.6gs;%.6g;%.6g;; size=%dB;;;",
636 total_time,
637 warning_thresholds != NULL ? (double)thlds->warning->end : 0.0,
638 critical_thresholds != NULL ? (double)thlds->critical->end : 0.0,
639 (int)body_buf.buflen);
640 }
641
642 /* return a CRITICAL status if we couldn't read any data */
643 if (strlen(header_buf.buf) == 0 && strlen(body_buf.buf) == 0)
644 die (STATE_CRITICAL, _("HTTP CRITICAL - No header received from host\n"));
645
646 /* get status line of answer, check sanity of HTTP code */
647 if (curlhelp_parse_statusline (header_buf.buf, &status_line) < 0) {
648 snprintf (msg, DEFAULT_BUFFER_SIZE, "Unparseable status line in %.3g seconds response time|%s\n",
649 code, total_time, perfstring);
650 die (STATE_CRITICAL, "HTTP CRITICAL HTTP/1.x %d unknown - %s", code, msg);
651 }
652
653 /* get result code from cURL */
654 handle_curl_option_return_code (curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &code), "CURLINFO_RESPONSE_CODE");
655 if (verbose>=2)
656 printf ("* curl CURLINFO_RESPONSE_CODE is %d\n", code);
657
658 /* print status line, header, body if verbose */
659 if (verbose >= 2) {
660 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header_buf.buf,
661 (no_body ? " [[ skipped ]]" : body_buf.buf));
662 }
663
664 /* make sure the status line matches the response we are looking for */
665 if (!expected_statuscode(status_line.first_line, server_expect)) {
666 /* TODO: fix first_line being cut off */
667 if (server_port == HTTP_PORT)
668 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), status_line.first_line);
669 else
670 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), server_port, status_line.first_line);
671 die (STATE_CRITICAL, "HTTP CRITICAL - %s", msg);
672 }
673
674 /* TODO: implement -d header tests */
675 if( server_expect_yn ) {
676 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), server_expect);
677 if (verbose)
678 printf ("%s\n",msg);
679 result = STATE_OK;
680 }
681 else {
682 /* illegal return codes result in a critical state */
683 if (code >= 600 || code < 100) {
684 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), status_line.http_code, status_line.msg);
685 /* server errors result in a critical state */
686 } else if (code >= 500) {
687 result = STATE_CRITICAL;
688 /* client errors result in a warning state */
689 } else if (code >= 400) {
690 result = STATE_WARNING;
691 /* check redirected page if specified */
692 } else if (code >= 300) {
693 if (onredirect == STATE_DEPENDENT) {
694 code = status_line.http_code;
695 }
696 result = max_state_alt (onredirect, result);
697 /* TODO: make sure the last status line has been
698 parsed into the status_line structure
699 */
700 /* all other codes are considered ok */
701 } else {
702 result = STATE_OK;
703 }
704 }
705
706 /* check status codes, set exit status accordingly */
707 if( status_line.http_code != code ) {
708 die (STATE_CRITICAL, _("HTTP CRITICAL HTTP/%d.%d %d %s - different HTTP codes (cUrl has %ld)\n"),
709 status_line.http_major, status_line.http_minor,
710 status_line.http_code, status_line.msg, code);
711 }
712
713 if (maximum_age >= 0) {
714 result = max_state_alt(check_document_dates(&header_buf, &msg), result);
715 }
716
717 /* Page and Header content checks go here */
718
719 if (strlen (header_expect)) {
720 if (!strstr (header_buf.buf, header_expect)) {
721 strncpy(&output_header_search[0],header_expect,sizeof(output_header_search));
722 if(output_header_search[sizeof(output_header_search)-1]!='\0') {
723 bcopy("...",&output_header_search[sizeof(output_header_search)-4],4);
724 }
725 snprintf (msg, DEFAULT_BUFFER_SIZE, _("%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);
726 result = STATE_CRITICAL;
727 }
728 }
729
730 if (strlen (string_expect)) {
731 if (!strstr (body_buf.buf, string_expect)) {
732 strncpy(&output_string_search[0],string_expect,sizeof(output_string_search));
733 if(output_string_search[sizeof(output_string_search)-1]!='\0') {
734 bcopy("...",&output_string_search[sizeof(output_string_search)-4],4);
735 }
736 snprintf (msg, DEFAULT_BUFFER_SIZE, _("%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);
737 result = STATE_CRITICAL;
738 }
739 }
740
741 if (strlen (regexp)) {
742 errcode = regexec (&preg, body_buf.buf, REGS, pmatch, 0);
743 if ((errcode == 0 && invert_regex == 0) || (errcode == REG_NOMATCH && invert_regex == 1)) {
744 /* OK - No-op to avoid changing the logic around it */
745 result = max_state_alt(STATE_OK, result);
746 }
747 else if ((errcode == REG_NOMATCH && invert_regex == 0) || (errcode == 0 && invert_regex == 1)) {
748 if (invert_regex == 0)
749 snprintf (msg, DEFAULT_BUFFER_SIZE, _("%spattern not found, "), msg);
750 else
751 snprintf (msg, DEFAULT_BUFFER_SIZE, _("%spattern found, "), msg);
752 result = STATE_CRITICAL;
753 }
754 else {
755 /* FIXME: Shouldn't that be UNKNOWN? */
756 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
757 snprintf (msg, DEFAULT_BUFFER_SIZE, _("%sExecute Error: %s, "), msg, errbuf);
758 result = STATE_CRITICAL;
759 }
760 }
761
762 /* make sure the page is of an appropriate size
763 * TODO: as far I can tell check_http gets the full size of header and
764 * if -N is not given header+body. Does this make sense?
765 *
766 * TODO: check_http.c had a get_length function, the question is really
767 * here what to use? the raw data size of the header_buf, the value of
768 * Content-Length, both and warn if they differ? Should the length be
769 * header+body or only body?
770 *
771 * One possible policy:
772 * - use header_buf.buflen (warning, if it mismatches to the Content-Length value
773 * - if -N (nobody) is given, use Content-Length only and hope the server set
774 * the value correcly
775 */
776 page_len = get_content_length(&header_buf, &body_buf);
777 if ((max_page_len > 0) && (page_len > max_page_len)) {
778 snprintf (msg, DEFAULT_BUFFER_SIZE, _("%spage size %d too large, "), msg, page_len);
779 result = max_state_alt(STATE_WARNING, result);
780 } else if ((min_page_len > 0) && (page_len < min_page_len)) {
781 snprintf (msg, DEFAULT_BUFFER_SIZE, _("%spage size %d too small, "), msg, page_len);
782 result = max_state_alt(STATE_WARNING, result);
783 }
784
785 /* -w, -c: check warning and critical level */
786 result = max_state_alt(get_status(total_time, thlds), result);
787
788 /* Cut-off trailing characters */
789 if(msg[strlen(msg)-2] == ',')
790 msg[strlen(msg)-2] = '\0';
791 else
792 msg[strlen(msg)-3] = '\0';
793
794 /* TODO: separate _() msg and status code: die (result, "HTTP %s: %s\n", state_text(result), msg); */
795 die (result, "HTTP %s: HTTP/%d.%d %d %s%s%s - %d bytes in %.3f second response time %s|%s\n",
796 state_text(result), status_line.http_major, status_line.http_minor,
797 status_line.http_code, status_line.msg,
798 strlen(msg) > 0 ? " - " : "",
799 msg, page_len, total_time,
800 (display_html ? "</A>" : ""),
801 perfstring);
802
803 /* proper cleanup after die? */
804 curlhelp_free_statusline(&status_line);
805 curl_easy_cleanup (curl);
806 curl_global_cleanup ();
807 curlhelp_freewritebuffer (&body_buf);
808 curlhelp_freewritebuffer (&header_buf);
809 if (!strcmp (http_method, "PUT")) {
810 curlhelp_freereadbuffer (&put_buf);
811 }
812
813 return result;
814}
815
816/* check whether a file exists */
817void
818test_file (char *path)
819{
820 if (access(path, R_OK) == 0)
821 return;
822 usage2 (_("file does not exist or is not readable"), path);
823}
824
825int
826process_arguments (int argc, char **argv)
827{
828 char *p;
829 int c = 1;
830 char *temp;
831
832 enum {
833 INVERT_REGEX = CHAR_MAX + 1,
834 SNI_OPTION,
835 CA_CERT_OPTION
836 };
837
838 int option = 0;
839 int got_plus = 0;
840 static struct option longopts[] = {
841 STD_LONG_OPTS,
842 {"link", no_argument, 0, 'L'},
843 {"nohtml", no_argument, 0, 'n'},
844 {"ssl", optional_argument, 0, 'S'},
845 {"sni", no_argument, 0, SNI_OPTION},
846 {"post", required_argument, 0, 'P'},
847 {"method", required_argument, 0, 'j'},
848 {"IP-address", required_argument, 0, 'I'},
849 {"url", required_argument, 0, 'u'},
850 {"port", required_argument, 0, 'p'},
851 {"authorization", required_argument, 0, 'a'},
852 {"header-string", required_argument, 0, 'd'},
853 {"string", required_argument, 0, 's'},
854 {"expect", required_argument, 0, 'e'},
855 {"regex", required_argument, 0, 'r'},
856 {"ereg", required_argument, 0, 'r'},
857 {"eregi", required_argument, 0, 'R'},
858 {"linespan", no_argument, 0, 'l'},
859 {"onredirect", required_argument, 0, 'f'},
860 {"certificate", required_argument, 0, 'C'},
861 {"client-cert", required_argument, 0, 'J'},
862 {"private-key", required_argument, 0, 'K'},
863 {"ca-cert", required_argument, 0, CA_CERT_OPTION},
864 {"useragent", required_argument, 0, 'A'},
865 {"header", required_argument, 0, 'k'},
866 {"no-body", no_argument, 0, 'N'},
867 {"max-age", required_argument, 0, 'M'},
868 {"content-type", required_argument, 0, 'T'},
869 {"pagesize", required_argument, 0, 'm'},
870 {"invert-regex", no_argument, NULL, INVERT_REGEX},
871 {"use-ipv4", no_argument, 0, '4'},
872 {"use-ipv6", no_argument, 0, '6'},
873 {"extended-perfdata", no_argument, 0, 'E'},
874 {0, 0, 0, 0}
875 };
876
877 if (argc < 2)
878 return ERROR;
879
880 /* support check_http compatible arguments */
881 for (c = 1; c < argc; c++) {
882 if (strcmp ("-to", argv[c]) == 0)
883 strcpy (argv[c], "-t");
884 if (strcmp ("-hn", argv[c]) == 0)
885 strcpy (argv[c], "-H");
886 if (strcmp ("-wt", argv[c]) == 0)
887 strcpy (argv[c], "-w");
888 if (strcmp ("-ct", argv[c]) == 0)
889 strcpy (argv[c], "-c");
890 if (strcmp ("-nohtml", argv[c]) == 0)
891 strcpy (argv[c], "-n");
892 }
893
894 while (1) {
895 c = getopt_long (argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:d:e:p:s:R:r:u:f:C:J:K:nlLS::m:M:NE", longopts, &option);
896 if (c == -1 || c == EOF || c == 1)
897 break;
898
899 switch (c) {
900 case 'h':
901 print_help();
902 exit(STATE_UNKNOWN);
903 break;
904 case 'V':
905 print_revision(progname, NP_VERSION);
906 print_curl_version();
907 exit(STATE_UNKNOWN);
908 break;
909 case 'v':
910 verbose++;
911 break;
912 case 't': /* timeout period */
913 if (!is_intnonneg (optarg))
914 usage2 (_("Timeout interval must be a positive integer"), optarg);
915 else
916 socket_timeout = (int)strtol (optarg, NULL, 10);
917 break;
918 case 'c': /* critical time threshold */
919 critical_thresholds = optarg;
920 break;
921 case 'w': /* warning time threshold */
922 warning_thresholds = optarg;
923 break;
924 case 'H': /* virtual host */
925 host_name = strdup (optarg);
926 if (host_name[0] == '[') {
927 if ((p = strstr (host_name, "]:")) != NULL) { /* [IPv6]:port */
928 virtual_port = atoi (p + 2);
929 /* cut off the port */
930 host_name_length = strlen (host_name) - strlen (p) - 1;
931 free (host_name);
932 host_name = strndup (optarg, host_name_length);
933 }
934 } else if ((p = strchr (host_name, ':')) != NULL
935 && strchr (++p, ':') == NULL) { /* IPv4:port or host:port */
936 virtual_port = atoi (p);
937 /* cut off the port */
938 host_name_length = strlen (host_name) - strlen (p) - 1;
939 free (host_name);
940 host_name = strndup (optarg, host_name_length);
941 }
942 break;
943 case 'I': /* internet address */
944 server_address = strdup (optarg);
945 break;
946 case 'u': /* URL path */
947 server_url = strdup (optarg);
948 break;
949 case 'p': /* Server port */
950 if (!is_intnonneg (optarg))
951 usage2 (_("Invalid port number, expecting a non-negative number"), optarg);
952 else {
953 if( strtol(optarg, NULL, 10) > MAX_PORT)
954 usage2 (_("Invalid port number, supplied port number is too big"), optarg);
955 server_port = (unsigned short)strtol(optarg, NULL, 10);
956 }
957 break;
958 case 'a': /* authorization info */
959 strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
960 user_auth[MAX_INPUT_BUFFER - 1] = 0;
961 break;
962 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
963 if (! http_post_data)
964 http_post_data = strdup (optarg);
965 if (! http_method)
966 http_method = strdup("POST");
967 break;
968 case 'j': /* Set HTTP method */
969 if (http_method)
970 free(http_method);
971 http_method = strdup (optarg);
972 break;
973 case 'A': /* useragent */
974 snprintf (user_agent, DEFAULT_BUFFER_SIZE, optarg);
975 break;
976 case 'k': /* Additional headers */
977 header_list = curl_slist_append(header_list, optarg);
978 break;
979 case 'L': /* show html link */
980 display_html = TRUE;
981 break;
982 case 'n': /* do not show html link */
983 display_html = FALSE;
984 break;
985 case 'C': /* Check SSL cert validity */
986#ifdef LIBCURL_FEATURE_SSL
987 if ((temp=strchr(optarg,','))!=NULL) {
988 *temp='\0';
989 if (!is_intnonneg (optarg))
990 usage2 (_("Invalid certificate expiration period"), optarg);
991 days_till_exp_warn = atoi(optarg);
992 *temp=',';
993 temp++;
994 if (!is_intnonneg (temp))
995 usage2 (_("Invalid certificate expiration period"), temp);
996 days_till_exp_crit = atoi (temp);
997 }
998 else {
999 days_till_exp_crit=0;
1000 if (!is_intnonneg (optarg))
1001 usage2 (_("Invalid certificate expiration period"), optarg);
1002 days_till_exp_warn = atoi (optarg);
1003 }
1004 check_cert = TRUE;
1005 goto enable_ssl;
1006#endif
1007 case 'J': /* use client certificate */
1008#ifdef LIBCURL_FEATURE_SSL
1009 test_file(optarg);
1010 client_cert = optarg;
1011 goto enable_ssl;
1012#endif
1013 case 'K': /* use client private key */
1014#ifdef LIBCURL_FEATURE_SSL
1015 test_file(optarg);
1016 client_privkey = optarg;
1017 goto enable_ssl;
1018#endif
1019#ifdef LIBCURL_FEATURE_SSL
1020 case CA_CERT_OPTION: /* use CA chain file */
1021 test_file(optarg);
1022 ca_cert = optarg;
1023 goto enable_ssl;
1024#endif
1025 case 'S': /* use SSL */
1026#ifdef LIBCURL_FEATURE_SSL
1027 enable_ssl:
1028 use_ssl = TRUE;
1029 /* ssl_version initialized to CURL_SSLVERSION_TLSv1_0 as a default.
1030 * Only set if it's non-zero. This helps when we include multiple
1031 * parameters, like -S and -C combinations */
1032 ssl_version = CURL_SSLVERSION_TLSv1_0;
1033 if (c=='S' && optarg != NULL) {
1034 char *plus_ptr = strchr(optarg, '+');
1035 if (plus_ptr) {
1036 got_plus = 1;
1037 *plus_ptr = '\0';
1038 }
1039
1040 if (optarg[0] == '2')
1041 ssl_version = CURL_SSLVERSION_SSLv2;
1042 else if (optarg[0] == '3')
1043 ssl_version = CURL_SSLVERSION_SSLv3;
1044 else if (!strcmp (optarg, "1") || !strcmp (optarg, "1.0"))
1045#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1046 ssl_version = CURL_SSLVERSION_TLSv1_0;
1047#else
1048 ssl_version = CURL_SSLVERSION_DEFAULT;
1049#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1050 else if (!strcmp (optarg, "1.1"))
1051#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1052 ssl_version = CURL_SSLVERSION_TLSv1_1;
1053#else
1054 ssl_version = CURL_SSLVERSION_DEFAULT;
1055#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1056 else if (!strcmp (optarg, "1.2"))
1057#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1058 ssl_version = CURL_SSLVERSION_TLSv1_2;
1059#else
1060 ssl_version = CURL_SSLVERSION_DEFAULT;
1061#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1062 else if (!strcmp (optarg, "1.3"))
1063#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0)
1064 ssl_version = CURL_SSLVERSION_TLSv1_3;
1065#else
1066 ssl_version = CURL_SSLVERSION_DEFAULT;
1067#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */
1068 else
1069 usage4 (_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2 (with optional '+' suffix)"));
1070 }
1071#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0)
1072 if (got_plus) {
1073 switch (ssl_version) {
1074 case CURL_SSLVERSION_TLSv1_3:
1075 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1076 break;
1077 case CURL_SSLVERSION_TLSv1_2:
1078 case CURL_SSLVERSION_TLSv1_1:
1079 case CURL_SSLVERSION_TLSv1_0:
1080 ssl_version |= CURL_SSLVERSION_MAX_DEFAULT;
1081 break;
1082 }
1083 } else {
1084 switch (ssl_version) {
1085 case CURL_SSLVERSION_TLSv1_3:
1086 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1087 break;
1088 case CURL_SSLVERSION_TLSv1_2:
1089 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2;
1090 break;
1091 case CURL_SSLVERSION_TLSv1_1:
1092 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1;
1093 break;
1094 case CURL_SSLVERSION_TLSv1_0:
1095 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0;
1096 break;
1097 }
1098 }
1099#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */
1100 if (verbose >= 2)
1101 printf(_("* Set SSL/TLS version to %d\n"), ssl_version);
1102 if (server_port == HTTP_PORT)
1103 server_port = HTTPS_PORT;
1104 break;
1105#else /* LIBCURL_FEATURE_SSL */
1106 /* -C -J and -K fall through to here without SSL */
1107 usage4 (_("Invalid option - SSL is not available"));
1108 break;
1109 case SNI_OPTION: /* --sni is parsed, but ignored, the default is TRUE with libcurl */
1110 use_sni = TRUE;
1111 break;
1112#endif /* LIBCURL_FEATURE_SSL */
1113 case 'f': /* onredirect */
1114 if (!strcmp (optarg, "ok"))
1115 onredirect = STATE_OK;
1116 else if (!strcmp (optarg, "warning"))
1117 onredirect = STATE_WARNING;
1118 else if (!strcmp (optarg, "critical"))
1119 onredirect = STATE_CRITICAL;
1120 else if (!strcmp (optarg, "unknown"))
1121 onredirect = STATE_UNKNOWN;
1122 else if (!strcmp (optarg, "follow"))
1123 onredirect = STATE_DEPENDENT;
1124 else usage2 (_("Invalid onredirect option"), optarg);
1125 if (verbose >= 2)
1126 printf(_("* Following redirects set to %s\n"), state_text(onredirect));
1127 break;
1128 case 'd': /* string or substring */
1129 strncpy (header_expect, optarg, MAX_INPUT_BUFFER - 1);
1130 header_expect[MAX_INPUT_BUFFER - 1] = 0;
1131 break;
1132 case 's': /* string or substring */
1133 strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
1134 string_expect[MAX_INPUT_BUFFER - 1] = 0;
1135 break;
1136 case 'e': /* string or substring */
1137 strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
1138 server_expect[MAX_INPUT_BUFFER - 1] = 0;
1139 server_expect_yn = 1;
1140 break;
1141 case 'T': /* Content-type */
1142 http_content_type = strdup (optarg);
1143 break;
1144 case 'l': /* linespan */
1145 cflags &= ~REG_NEWLINE;
1146 break;
1147 case 'R': /* regex */
1148 cflags |= REG_ICASE;
1149 case 'r': /* regex */
1150 strncpy (regexp, optarg, MAX_RE_SIZE - 1);
1151 regexp[MAX_RE_SIZE - 1] = 0;
1152 errcode = regcomp (&preg, regexp, cflags);
1153 if (errcode != 0) {
1154 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1155 printf (_("Could Not Compile Regular Expression: %s"), errbuf);
1156 return ERROR;
1157 }
1158 break;
1159 case INVERT_REGEX:
1160 invert_regex = 1;
1161 break;
1162 case '4':
1163 address_family = AF_INET;
1164 break;
1165 case '6':
1166#if defined (USE_IPV6) && defined (LIBCURL_FEATURE_IPV6)
1167 address_family = AF_INET6;
1168#else
1169 usage4 (_("IPv6 support not available"));
1170#endif
1171 break;
1172 case 'm': /* min_page_length */
1173 {
1174 char *tmp;
1175 if (strchr(optarg, ':') != (char *)NULL) {
1176 /* range, so get two values, min:max */
1177 tmp = strtok(optarg, ":");
1178 if (tmp == NULL) {
1179 printf("Bad format: try \"-m min:max\"\n");
1180 exit (STATE_WARNING);
1181 } else
1182 min_page_len = atoi(tmp);
1183
1184 tmp = strtok(NULL, ":");
1185 if (tmp == NULL) {
1186 printf("Bad format: try \"-m min:max\"\n");
1187 exit (STATE_WARNING);
1188 } else
1189 max_page_len = atoi(tmp);
1190 } else
1191 min_page_len = atoi (optarg);
1192 break;
1193 }
1194 case 'N': /* no-body */
1195 no_body = TRUE;
1196 break;
1197 case 'M': /* max-age */
1198 {
1199 int L = strlen(optarg);
1200 if (L && optarg[L-1] == 'm')
1201 maximum_age = atoi (optarg) * 60;
1202 else if (L && optarg[L-1] == 'h')
1203 maximum_age = atoi (optarg) * 60 * 60;
1204 else if (L && optarg[L-1] == 'd')
1205 maximum_age = atoi (optarg) * 60 * 60 * 24;
1206 else if (L && (optarg[L-1] == 's' ||
1207 isdigit (optarg[L-1])))
1208 maximum_age = atoi (optarg);
1209 else {
1210 fprintf (stderr, "unparsable max-age: %s\n", optarg);
1211 exit (STATE_WARNING);
1212 }
1213 if (verbose >= 2)
1214 printf ("* Maximal age of document set to %d seconds\n", maximum_age);
1215 }
1216 break;
1217 case 'E': /* show extended perfdata */
1218 show_extended_perfdata = TRUE;
1219 break;
1220 case '?':
1221 /* print short usage statement if args not parsable */
1222 usage5 ();
1223 break;
1224 }
1225 }
1226
1227 c = optind;
1228
1229 if (server_address == NULL && c < argc)
1230 server_address = strdup (argv[c++]);
1231
1232 if (host_name == NULL && c < argc)
1233 host_name = strdup (argv[c++]);
1234
1235 if (server_address == NULL) {
1236 if (host_name == NULL)
1237 usage4 (_("You must specify a server address or host name"));
1238 else
1239 server_address = strdup (host_name);
1240 }
1241
1242 set_thresholds(&thlds, warning_thresholds, critical_thresholds);
1243
1244 if (critical_thresholds && thlds->critical->end>(double)socket_timeout)
1245 socket_timeout = (int)thlds->critical->end + 1;
1246 if (verbose >= 2)
1247 printf ("* Socket timeout set to %d seconds\n", socket_timeout);
1248
1249 if (http_method == NULL)
1250 http_method = strdup ("GET");
1251
1252 if (client_cert && !client_privkey)
1253 usage4 (_("If you use a client certificate you must also specify a private key file"));
1254
1255 if (virtual_port == 0)
1256 virtual_port = server_port;
1257
1258 return TRUE;
1259}
1260
1261void
1262print_help (void)
1263{
1264 print_revision (progname, NP_VERSION);
1265
1266 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1267 printf ("Copyright (c) 2017 Andreas Baumann <mail@andreasbaumann.cc>\n");
1268 printf (COPYRIGHT, copyright, email);
1269
1270 printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
1271 printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
1272 printf ("%s\n", _("strings and regular expressions, check connection times, and report on"));
1273 printf ("%s\n", _("certificate expiration times."));
1274 printf ("\n");
1275 printf ("%s\n", _("It makes use of libcurl to do so. It tries to be as compatible to check_http"));
1276 printf ("%s\n", _("as possible."));
1277
1278 printf ("\n\n");
1279
1280 print_usage ();
1281
1282 printf (_("NOTE: One or both of -H and -I must be specified"));
1283
1284 printf ("\n");
1285
1286 printf (UT_HELP_VRSN);
1287 printf (UT_EXTRA_OPTS);
1288
1289 printf (" %s\n", "-H, --hostname=ADDRESS");
1290 printf (" %s\n", _("Host name argument for servers using host headers (virtual host)"));
1291 printf (" %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1292 printf (" %s\n", "-I, --IP-address=ADDRESS");
1293 printf (" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1294 printf (" %s\n", "-p, --port=INTEGER");
1295 printf (" %s", _("Port number (default: "));
1296 printf ("%d)\n", HTTP_PORT);
1297
1298 printf (UT_IPv46);
1299
1300#ifdef LIBCURL_FEATURE_SSL
1301 printf (" %s\n", "-S, --ssl=VERSION[+]");
1302 printf (" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents"));
1303 printf (" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,"));
1304 printf (" %s\n", _("1.2 = TLSv1.2). With a '+' suffix, newer versions are also accepted."));
1305 printf (" %s\n", _("Note: SSLv2 and SSLv3 are deprecated and are usually disabled in libcurl"));
1306 printf (" %s\n", "--sni");
1307 printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
1308#if LIBCURL_VERSION_NUM >= 0x071801
1309 printf (" %s\n", _("Note: --sni is the default in libcurl as SSLv2 and SSLV3 are deprecated and"));
1310 printf (" %s\n", _(" SNI only really works since TLSv1.0"));
1311#else
1312 printf (" %s\n", _("Note: SNI is not supported in libcurl before 7.18.1"));
1313#endif
1314 printf (" %s\n", "-C, --certificate=INTEGER[,INTEGER]");
1315 printf (" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
1316 printf (" %s\n", _("(when this option is used the URL is not checked.)"));
1317 printf (" %s\n", "-J, --client-cert=FILE");
1318 printf (" %s\n", _("Name of file that contains the client certificate (PEM format)"));
1319 printf (" %s\n", _("to be used in establishing the SSL session"));
1320 printf (" %s\n", "-K, --private-key=FILE");
1321 printf (" %s\n", _("Name of file containing the private key (PEM format)"));
1322 printf (" %s\n", _("matching the client certificate"));
1323 printf (" %s\n", "--ca-cert=FILE");
1324 printf (" %s\n", _("CA certificate file to verify peer against"));
1325#endif
1326
1327 printf (" %s\n", "-e, --expect=STRING");
1328 printf (" %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1329 printf (" %s", _("the first (status) line of the server response (default: "));
1330 printf ("%s)\n", HTTP_EXPECT);
1331 printf (" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1332 printf (" %s\n", "-d, --header-string=STRING");
1333 printf (" %s\n", _("String to expect in the response headers"));
1334 printf (" %s\n", "-s, --string=STRING");
1335 printf (" %s\n", _("String to expect in the content"));
1336 printf (" %s\n", "-u, --url=PATH");
1337 printf (" %s\n", _("URL to GET or POST (default: /)"));
1338 printf (" %s\n", "-P, --post=STRING");
1339 printf (" %s\n", _("URL encoded http POST data"));
1340 printf (" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT)");
1341 printf (" %s\n", _("Set HTTP method."));
1342 printf (" %s\n", "-N, --no-body");
1343 printf (" %s\n", _("Don't wait for document body: stop reading after headers."));
1344 printf (" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
1345 printf (" %s\n", "-M, --max-age=SECONDS");
1346 printf (" %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
1347 printf (" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1348 printf (" %s\n", "-T, --content-type=STRING");
1349 printf (" %s\n", _("specify Content-Type header media type when POSTing\n"));
1350 printf (" %s\n", "-l, --linespan");
1351 printf (" %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1352 printf (" %s\n", "-r, --regex, --ereg=STRING");
1353 printf (" %s\n", _("Search page for regex STRING"));
1354 printf (" %s\n", "-R, --eregi=STRING");
1355 printf (" %s\n", _("Search page for case-insensitive regex STRING"));
1356 printf (" %s\n", "--invert-regex");
1357 printf (" %s\n", _("Return CRITICAL if found, OK if not\n"));
1358 printf (" %s\n", "-a, --authorization=AUTH_PAIR");
1359 printf (" %s\n", _("Username:password on sites with basic authentication"));
1360 printf (" %s\n", "-A, --useragent=STRING");
1361 printf (" %s\n", _("String to be sent in http header as \"User Agent\""));
1362 printf (" %s\n", "-k, --header=STRING");
1363 printf (" %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers"));
1364 printf (" %s\n", "-E, --extended-perfdata");
1365 printf (" %s\n", _("Print additional performance data"));
1366 printf (" %s\n", "-L, --link");
1367 printf (" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1368 printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow>");
1369 printf (" %s\n", _("How to handle redirected pages."));
1370 printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1371 printf (" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1372
1373 printf (UT_WARN_CRIT);
1374
1375 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1376
1377 printf (UT_VERBOSE);
1378
1379 printf ("\n");
1380 printf ("%s\n", _("Notes:"));
1381 printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1382 printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1383 printf (" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response"));
1384 printf (" %s\n", _("messages from the host result in STATE_WARNING return values. If you are"));
1385 printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1386 printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
1387
1388#ifdef LIBCURL_FEATURE_SSL
1389 printf ("\n");
1390 printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
1391 printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
1392 printf (" %s\n", _("certificate is still valid for the specified number of days."));
1393 printf ("\n");
1394 printf (" %s\n", _("Please note that this plugin does not check if the presented server"));
1395 printf (" %s\n", _("certificate matches the hostname of the server, or if the certificate"));
1396 printf (" %s\n", _("has a valid chain of trust to one of the locally installed CAs."));
1397 printf ("\n");
1398 printf ("%s\n", _("Examples:"));
1399 printf (" %s\n\n", "CHECK CONTENT: check_curl -w 5 -c 10 --ssl -H www.verisign.com");
1400 printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
1401 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1402 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1403 printf (" %s\n", _("a STATE_CRITICAL will be returned."));
1404 printf ("\n");
1405 printf (" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 14");
1406 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1407 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1408 printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
1409 printf (" %s\n\n", _("the certificate is expired."));
1410 printf ("\n");
1411 printf (" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 30,14");
1412 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 30 days,"));
1413 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1414 printf (" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned."));
1415 printf (" %s\n", _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days"));
1416
1417 printf (" %s\n\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: ");
1418 printf (" %s\n", _("check_curl -I 192.168.100.35 -p 80 -u https://www.verisign.com/ -S -j CONNECT -H www.verisign.com "));
1419 printf (" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> -S(sl) -j CONNECT -H <webserver>"));
1420 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1421 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1422 printf (" %s\n", _("a STATE_CRITICAL will be returned."));
1423
1424#endif
1425
1426 printf (UT_SUPPORT);
1427
1428}
1429
1430
1431
1432void
1433print_usage (void)
1434{
1435 printf ("%s\n", _("Usage:"));
1436 printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname);
1437 printf (" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate file>]\n");
1438 printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n");
1439 printf (" [-f <ok|warning|critcal|follow>]\n");
1440 printf (" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
1441 printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
1442 printf (" [-A string] [-k string] [-S <version>] [--sni] [-C <warn_age>[,<crit_age>]]\n");
1443 printf (" [-T <content-type>] [-j method]\n", progname);
1444 printf ("\n");
1445 printf ("%s\n", _("WARNING: check_curl is experimental. Please use"));
1446 printf ("%s\n\n", _("check_http if you need a stable version."));
1447}
1448
1449void
1450print_curl_version (void)
1451{
1452 printf( "%s\n", curl_version());
1453}
1454
1455int
1456curlhelp_initwritebuffer (curlhelp_write_curlbuf *buf)
1457{
1458 buf->bufsize = DEFAULT_BUFFER_SIZE;
1459 buf->buflen = 0;
1460 buf->buf = (char *)malloc ((size_t)buf->bufsize);
1461 if (buf->buf == NULL) return -1;
1462 return 0;
1463}
1464
1465int
1466curlhelp_buffer_write_callback (void *buffer, size_t size, size_t nmemb, void *stream)
1467{
1468 curlhelp_write_curlbuf *buf = (curlhelp_write_curlbuf *)stream;
1469
1470 while (buf->bufsize < buf->buflen + size * nmemb + 1) {
1471 buf->bufsize *= buf->bufsize * 2;
1472 buf->buf = (char *)realloc (buf->buf, buf->bufsize);
1473 if (buf->buf == NULL) return -1;
1474 }
1475
1476 memcpy (buf->buf + buf->buflen, buffer, size * nmemb);
1477 buf->buflen += size * nmemb;
1478 buf->buf[buf->buflen] = '\0';
1479
1480 return (int)(size * nmemb);
1481}
1482
1483int
1484curlhelp_buffer_read_callback (void *buffer, size_t size, size_t nmemb, void *stream)
1485{
1486 curlhelp_read_curlbuf *buf = (curlhelp_read_curlbuf *)stream;
1487
1488 size_t n = min (nmemb * size, buf->buflen - buf->pos);
1489
1490 memcpy (buffer, buf->buf + buf->pos, n);
1491 buf->pos += n;
1492
1493 return (int)n;
1494}
1495
1496void
1497curlhelp_freewritebuffer (curlhelp_write_curlbuf *buf)
1498{
1499 free (buf->buf);
1500 buf->buf = NULL;
1501}
1502
1503int
1504curlhelp_initreadbuffer (curlhelp_read_curlbuf *buf, const char *data, size_t datalen)
1505{
1506 buf->buflen = datalen;
1507 buf->buf = (char *)malloc ((size_t)buf->buflen);
1508 if (buf->buf == NULL) return -1;
1509 memcpy (buf->buf, data, datalen);
1510 buf->pos = 0;
1511 return 0;
1512}
1513
1514void
1515curlhelp_freereadbuffer (curlhelp_read_curlbuf *buf)
1516{
1517 free (buf->buf);
1518 buf->buf = NULL;
1519}
1520
1521/* TODO: where to put this, it's actually part of sstrings2 (logically)?
1522 */
1523const char*
1524strrstr2(const char *haystack, const char *needle)
1525{
1526 int counter;
1527 size_t len;
1528 const char *prev_pos;
1529 const char *pos;
1530
1531 if (haystack == NULL || needle == NULL)
1532 return NULL;
1533
1534 if (haystack[0] == '\0' || needle[0] == '\0')
1535 return NULL;
1536
1537 counter = 0;
1538 prev_pos = NULL;
1539 pos = haystack;
1540 len = strlen (needle);
1541 for (;;) {
1542 pos = strstr (pos, needle);
1543 if (pos == NULL) {
1544 if (counter == 0)
1545 return NULL;
1546 else
1547 return prev_pos;
1548 }
1549 counter++;
1550 prev_pos = pos;
1551 pos += len;
1552 if (*pos == '\0') return prev_pos;
1553 }
1554}
1555
1556int
1557curlhelp_parse_statusline (const char *buf, curlhelp_statusline *status_line)
1558{
1559 char *first_line_end;
1560 char *p;
1561 size_t first_line_len;
1562 char *pp;
1563 const char *start;
1564 char *first_line_buf;
1565
1566 /* find last start of a new header */
1567 start = strrstr2 (buf, "\r\nHTTP");
1568 if (start != NULL) {
1569 start += 2;
1570 buf = start;
1571 }
1572
1573 first_line_end = strstr(buf, "\r\n");
1574 if (first_line_end == NULL) return -1;
1575
1576 first_line_len = (size_t)(first_line_end - buf);
1577 status_line->first_line = (char *)malloc (first_line_len + 1);
1578 if (status_line->first_line == NULL) return -1;
1579 memcpy (status_line->first_line, buf, first_line_len);
1580 status_line->first_line[first_line_len] = '\0';
1581 first_line_buf = strdup( status_line->first_line );
1582
1583 /* protocol and version: "HTTP/x.x" SP */
1584
1585 p = strtok(first_line_buf, "/");
1586 if( p == NULL ) { free( first_line_buf ); return -1; }
1587 if( strcmp( p, "HTTP" ) != 0 ) { free( first_line_buf ); return -1; }
1588
1589 p = strtok( NULL, "." );
1590 if( p == NULL ) { free( first_line_buf ); return -1; }
1591 status_line->http_major = (int)strtol( p, &pp, 10 );
1592 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
1593
1594 p = strtok( NULL, " " );
1595 if( p == NULL ) { free( first_line_buf ); return -1; }
1596 status_line->http_minor = (int)strtol( p, &pp, 10 );
1597 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
1598
1599 /* status code: "404" or "404.1", then SP */
1600
1601 p = strtok( NULL, " ." );
1602 if( p == NULL ) { free( first_line_buf ); return -1; }
1603 if( strchr( p, '.' ) != NULL ) {
1604 char *ppp;
1605 ppp = strtok( p, "." );
1606 status_line->http_code = (int)strtol( ppp, &pp, 10 );
1607 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
1608
1609 ppp = strtok( NULL, "" );
1610 status_line->http_subcode = (int)strtol( ppp, &pp, 10 );
1611 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
1612 } else {
1613 status_line->http_code = (int)strtol( p, &pp, 10 );
1614 status_line->http_subcode = -1;
1615 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
1616 }
1617
1618 /* Human readable message: "Not Found" CRLF */
1619
1620 free( first_line_buf );
1621 p = strtok( NULL, "" );
1622 if( p == NULL ) { free( status_line->first_line ); return -1; }
1623 status_line->msg = status_line->first_line + ( p - first_line_buf );
1624
1625 return 0;
1626}
1627
1628void
1629curlhelp_free_statusline (curlhelp_statusline *status_line)
1630{
1631 free (status_line->first_line);
1632}
1633
1634void
1635remove_newlines (char *s)
1636{
1637 char *p;
1638
1639 for (p = s; *p != '\0'; p++)
1640 if (*p == '\r' || *p == '\n')
1641 *p = ' ';
1642}
1643
1644char *
1645perfd_time_ssl (double elapsed_time_ssl)
1646{
1647 return fperfdata ("time_ssl", elapsed_time_ssl, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0);
1648}
1649
1650char *
1651get_header_value (const struct phr_header* headers, const size_t nof_headers, const char* header)
1652{
1653 int i;
1654 for( i = 0; i < nof_headers; i++ ) {
1655 if( strncasecmp( header, headers[i].name, max( headers[i].name_len, 4 ) ) == 0 ) {
1656 return strndup( headers[i].value, headers[i].value_len );
1657 }
1658 }
1659 return NULL;
1660}
1661
1662int
1663check_document_dates (const curlhelp_write_curlbuf *header_buf, char (*msg)[DEFAULT_BUFFER_SIZE])
1664{
1665 char *server_date = NULL;
1666 char *document_date = NULL;
1667 int date_result = STATE_OK;
1668 curlhelp_statusline status_line;
1669 struct phr_header headers[255];
1670 size_t nof_headers = 255;
1671 size_t msglen;
1672
1673 int res = phr_parse_response (header_buf->buf, header_buf->buflen,
1674 &status_line.http_minor, &status_line.http_code, &status_line.msg, &msglen,
1675 headers, &nof_headers, 0);
1676
1677 server_date = get_header_value (headers, nof_headers, "date");
1678 document_date = get_header_value (headers, nof_headers, "last-modified");
1679
1680 if (!server_date || !*server_date) {
1681 snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sServer date unknown, "), *msg);
1682 date_result = max_state_alt(STATE_UNKNOWN, date_result);
1683 } else if (!document_date || !*document_date) {
1684 snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sDocument modification date unknown, "), *msg);
1685 date_result = max_state_alt(STATE_CRITICAL, date_result);
1686 } else {
1687 time_t srv_data = curl_getdate (server_date, NULL);
1688 time_t doc_data = curl_getdate (document_date, NULL);
1689 if (verbose >= 2)
1690 printf ("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, srv_data, document_date, doc_data);
1691 if (srv_data <= 0) {
1692 snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sServer date \"%100s\" unparsable, "), *msg, server_date);
1693 date_result = max_state_alt(STATE_CRITICAL, date_result);
1694 } else if (doc_data <= 0) {
1695 snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date);
1696 date_result = max_state_alt(STATE_CRITICAL, date_result);
1697 } else if (doc_data > srv_data + 30) {
1698 snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data);
1699 date_result = max_state_alt(STATE_CRITICAL, date_result);
1700 } else if (doc_data < srv_data - maximum_age) {
1701 int n = (srv_data - doc_data);
1702 if (n > (60 * 60 * 24 * 2)) {
1703 snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), *msg, ((float) n) / (60 * 60 * 24));
1704 date_result = max_state_alt(STATE_CRITICAL, date_result);
1705 } else {
1706 snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60);
1707 date_result = max_state_alt(STATE_CRITICAL, date_result);
1708 }
1709 }
1710 }
1711
1712 if (server_date) free (server_date);
1713 if (document_date) free (document_date);
1714
1715 return date_result;
1716}
1717
1718
1719int
1720get_content_length (const curlhelp_write_curlbuf* header_buf, const curlhelp_write_curlbuf* body_buf)
1721{
1722 const char *s;
1723 int content_length = 0;
1724 char *copy;
1725 struct phr_header headers[255];
1726 size_t nof_headers = 255;
1727 size_t msglen;
1728 char *content_length_s = NULL;
1729 curlhelp_statusline status_line;
1730
1731 int res = phr_parse_response (header_buf->buf, header_buf->buflen,
1732 &status_line.http_minor, &status_line.http_code, &status_line.msg, &msglen,
1733 headers, &nof_headers, 0);
1734
1735 content_length_s = get_header_value (headers, nof_headers, "content-length");
1736 if (!content_length_s) {
1737 return header_buf->buflen + body_buf->buflen;
1738 }
1739 content_length_s += strspn (content_length_s, " \t");
1740 content_length = atoi (content_length_s);
1741 if (content_length != body_buf->buflen) {
1742 /* TODO: should we warn if the actual and the reported body length doen't match? */
1743 }
1744
1745 if (content_length_s) free (content_length_s);
1746
1747 return header_buf->buflen + body_buf->buflen;
1748}
1749
1750/* TODO: is there a better way in libcurl to check for the SSL library? */
1751curlhelp_ssl_library
1752curlhelp_get_ssl_library (CURL* curl)
1753{
1754 curl_version_info_data* version_data;
1755 char *ssl_version;
1756 char *library;
1757 curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN;
1758
1759 version_data = curl_version_info (CURLVERSION_NOW);
1760 if (version_data == NULL) return CURLHELP_SSL_LIBRARY_UNKNOWN;
1761
1762 ssl_version = strdup (version_data->ssl_version);
1763 if (ssl_version == NULL ) return CURLHELP_SSL_LIBRARY_UNKNOWN;
1764
1765 library = strtok (ssl_version, "/");
1766 if (library == NULL) return CURLHELP_SSL_LIBRARY_UNKNOWN;
1767
1768 if (strcmp (library, "OpenSSL") == 0)
1769 ssl_library = CURLHELP_SSL_LIBRARY_OPENSSL;
1770 else if (strcmp (library, "LibreSSL") == 0)
1771 ssl_library = CURLHELP_SSL_LIBRARY_LIBRESSL;
1772 else if (strcmp (library, "GnuTLS") == 0)
1773 ssl_library = CURLHELP_SSL_LIBRARY_GNUTLS;
1774 else if (strcmp (library, "NSS") == 0)
1775 ssl_library = CURLHELP_SSL_LIBRARY_NSS;
1776
1777 if (verbose >= 2)
1778 printf ("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library, ssl_library);
1779
1780 free (ssl_version);
1781
1782 return ssl_library;
1783}
1784
1785const char*
1786curlhelp_get_ssl_library_string (curlhelp_ssl_library ssl_library)
1787{
1788 switch (ssl_library) {
1789 case CURLHELP_SSL_LIBRARY_OPENSSL:
1790 return "OpenSSL";
1791 case CURLHELP_SSL_LIBRARY_LIBRESSL:
1792 return "LibreSSL";
1793 case CURLHELP_SSL_LIBRARY_GNUTLS:
1794 return "GnuTLS";
1795 case CURLHELP_SSL_LIBRARY_NSS:
1796 return "NSS";
1797 case CURLHELP_SSL_LIBRARY_UNKNOWN:
1798 default:
1799 return "unknown";
1800 }
1801}
1802
1803#ifdef LIBCURL_FEATURE_SSL
1804#ifndef USE_OPENSSL
1805time_t
1806parse_cert_date (const char *s)
1807{
1808 struct tm tm;
1809 time_t date;
1810
1811 if (!s) return -1;
1812
1813 strptime (s, "%Y-%m-%d %H:%M:%S GMT", &tm);
1814 date = mktime (&tm);
1815
1816 return date;
1817}
1818
1819/* TODO: this needs cleanup in the sslutils.c, maybe we the #else case to
1820 * OpenSSL could be this function
1821 */
1822int
1823net_noopenssl_check_certificate (cert_ptr_union* cert_ptr, int days_till_exp_warn, int days_till_exp_crit)
1824{
1825 int i;
1826 struct curl_slist* slist;
1827 int cname_found = 0;
1828 char* start_date_str = NULL;
1829 char* end_date_str = NULL;
1830 time_t start_date;
1831 time_t end_date;
1832 char *tz;
1833 float time_left;
1834 int days_left;
1835 int time_remaining;
1836 char timestamp[50] = "";
1837 int status = STATE_UNKNOWN;
1838
1839 if (verbose >= 2)
1840 printf ("**** REQUEST CERTIFICATES ****\n");
1841
1842 for (i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) {
1843 for (slist = cert_ptr->to_certinfo->certinfo[i]; slist; slist = slist->next) {
1844 /* find first common name in subject, TODO: check alternative subjects for
1845 * multi-host certificate, check wildcards
1846 */
1847 if (strncasecmp (slist->data, "Subject:", 8) == 0) {
1848 char* p = strstr (slist->data, "CN=");
1849 if (p != NULL) {
1850 if (strncmp (host_name, p+3, strlen (host_name)) == 0) {
1851 cname_found = 1;
1852 }
1853 }
1854 } else if (strncasecmp (slist->data, "Start Date:", 11) == 0) {
1855 start_date_str = &slist->data[11];
1856 } else if (strncasecmp (slist->data, "Expire Date:", 12) == 0) {
1857 end_date_str = &slist->data[12];
1858 } else if (strncasecmp (slist->data, "Cert:", 5) == 0) {
1859 goto HAVE_FIRST_CERT;
1860 }
1861 if (verbose >= 2)
1862 printf ("%d ** %s\n", i, slist->data);
1863 }
1864 }
1865HAVE_FIRST_CERT:
1866
1867 if (verbose >= 2)
1868 printf ("**** REQUEST CERTIFICATES ****\n");
1869
1870 if (!cname_found) {
1871 printf("%s\n",_("CRITICAL - Cannot retrieve certificate subject."));
1872 return STATE_CRITICAL;
1873 }
1874
1875 start_date = parse_cert_date (start_date_str);
1876 if (start_date <= 0) {
1877 snprintf (msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Start Date' in certificate: '%s'"),
1878 start_date_str);
1879 puts (msg);
1880 return STATE_WARNING;
1881 }
1882
1883 end_date = parse_cert_date (end_date_str);
1884 if (end_date <= 0) {
1885 snprintf (msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Expire Date' in certificate: '%s'"),
1886 start_date_str);
1887 puts (msg);
1888 return STATE_WARNING;
1889 }
1890
1891 time_left = difftime (end_date, time(NULL));
1892 days_left = time_left / 86400;
1893 tz = getenv("TZ");
1894 setenv("TZ", "GMT", 1);
1895 tzset();
1896 strftime(timestamp, 50, "%c %z", localtime(&end_date));
1897 if (tz)
1898 setenv("TZ", tz, 1);
1899 else
1900 unsetenv("TZ");
1901 tzset();
1902
1903 if (days_left > 0 && days_left <= days_till_exp_warn) {
1904 printf (_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), (days_left>days_till_exp_crit)?"WARNING":"CRITICAL", host_name, days_left, timestamp);
1905 if (days_left > days_till_exp_crit)
1906 status = STATE_WARNING;
1907 else
1908 status = STATE_CRITICAL;
1909 } else if (days_left == 0 && time_left > 0) {
1910 if (time_left >= 3600)
1911 time_remaining = (int) time_left / 3600;
1912 else
1913 time_remaining = (int) time_left / 60;
1914
1915 printf (_("%s - Certificate '%s' expires in %u %s (%s)\n"),
1916 (days_left>days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, time_remaining,
1917 time_left >= 3600 ? "hours" : "minutes", timestamp);
1918
1919 if ( days_left > days_till_exp_crit)
1920 status = STATE_WARNING;
1921 else
1922 status = STATE_CRITICAL;
1923 } else if (time_left < 0) {
1924 printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), host_name, timestamp);
1925 status=STATE_CRITICAL;
1926 } else if (days_left == 0) {
1927 printf (_("%s - Certificate '%s' just expired (%s).\n"), (days_left>days_till_exp_crit)?"WARNING":"CRITICAL", host_name, timestamp);
1928 if (days_left > days_till_exp_crit)
1929 status = STATE_WARNING;
1930 else
1931 status = STATE_CRITICAL;
1932 } else {
1933 printf(_("OK - Certificate '%s' will expire on %s.\n"), host_name, timestamp);
1934 status = STATE_OK;
1935 }
1936 return status;
1937}
1938#endif /* USE_OPENSSL */
1939#endif /* LIBCURL_FEATURE_SSL */
diff --git a/plugins/picohttpparser.c b/plugins/picohttpparser.c
new file mode 100644
index 0000000..6a2d872
--- /dev/null
+++ b/plugins/picohttpparser.c
@@ -0,0 +1,620 @@
1/*
2 * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
3 * Shigeo Mitsunari
4 *
5 * The software is licensed under either the MIT License (below) or the Perl
6 * license.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to
10 * deal in the Software without restriction, including without limitation the
11 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12 * sell copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 * IN THE SOFTWARE.
25 */
26
27#include <assert.h>
28#include <stddef.h>
29#include <string.h>
30#ifdef __SSE4_2__
31#ifdef _MSC_VER
32#include <nmmintrin.h>
33#else
34#include <x86intrin.h>
35#endif
36#endif
37#include "picohttpparser.h"
38
39/* $Id: a707070d11d499609f99d09f97535642cec910a8 $ */
40
41#if __GNUC__ >= 3
42#define likely(x) __builtin_expect(!!(x), 1)
43#define unlikely(x) __builtin_expect(!!(x), 0)
44#else
45#define likely(x) (x)
46#define unlikely(x) (x)
47#endif
48
49#ifdef _MSC_VER
50#define ALIGNED(n) _declspec(align(n))
51#else
52#define ALIGNED(n) __attribute__((aligned(n)))
53#endif
54
55#define IS_PRINTABLE_ASCII(c) ((unsigned char)(c)-040u < 0137u)
56
57#define CHECK_EOF() \
58 if (buf == buf_end) { \
59 *ret = -2; \
60 return NULL; \
61 }
62
63#define EXPECT_CHAR_NO_CHECK(ch) \
64 if (*buf++ != ch) { \
65 *ret = -1; \
66 return NULL; \
67 }
68
69#define EXPECT_CHAR(ch) \
70 CHECK_EOF(); \
71 EXPECT_CHAR_NO_CHECK(ch);
72
73#define ADVANCE_TOKEN(tok, toklen) \
74 do { \
75 const char *tok_start = buf; \
76 static const char ALIGNED(16) ranges2[] = "\000\040\177\177"; \
77 int found2; \
78 buf = findchar_fast(buf, buf_end, ranges2, sizeof(ranges2) - 1, &found2); \
79 if (!found2) { \
80 CHECK_EOF(); \
81 } \
82 while (1) { \
83 if (*buf == ' ') { \
84 break; \
85 } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \
86 if ((unsigned char)*buf < '\040' || *buf == '\177') { \
87 *ret = -1; \
88 return NULL; \
89 } \
90 } \
91 ++buf; \
92 CHECK_EOF(); \
93 } \
94 tok = tok_start; \
95 toklen = buf - tok_start; \
96 } while (0)
97
98static const char *token_char_map = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
99 "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0"
100 "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1"
101 "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0"
102 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
103 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
104 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
105 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
106
107static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges, size_t ranges_size, int *found)
108{
109 *found = 0;
110#if __SSE4_2__
111 if (likely(buf_end - buf >= 16)) {
112 __m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges);
113
114 size_t left = (buf_end - buf) & ~15;
115 do {
116 __m128i b16 = _mm_loadu_si128((const __m128i *)buf);
117 int r = _mm_cmpestri(ranges16, ranges_size, b16, 16, _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS);
118 if (unlikely(r != 16)) {
119 buf += r;
120 *found = 1;
121 break;
122 }
123 buf += 16;
124 left -= 16;
125 } while (likely(left != 0));
126 }
127#else
128 /* suppress unused parameter warning */
129 (void)buf_end;
130 (void)ranges;
131 (void)ranges_size;
132#endif
133 return buf;
134}
135
136static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token, size_t *token_len, int *ret)
137{
138 const char *token_start = buf;
139
140#ifdef __SSE4_2__
141 static const char ranges1[] = "\0\010"
142 /* allow HT */
143 "\012\037"
144 /* allow SP and up to but not including DEL */
145 "\177\177"
146 /* allow chars w. MSB set */
147 ;
148 int found;
149 buf = findchar_fast(buf, buf_end, ranges1, sizeof(ranges1) - 1, &found);
150 if (found)
151 goto FOUND_CTL;
152#else
153 /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined */
154 while (likely(buf_end - buf >= 8)) {
155#define DOIT() \
156 do { \
157 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \
158 goto NonPrintable; \
159 ++buf; \
160 } while (0)
161 DOIT();
162 DOIT();
163 DOIT();
164 DOIT();
165 DOIT();
166 DOIT();
167 DOIT();
168 DOIT();
169#undef DOIT
170 continue;
171 NonPrintable:
172 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) {
173 goto FOUND_CTL;
174 }
175 ++buf;
176 }
177#endif
178 for (;; ++buf) {
179 CHECK_EOF();
180 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) {
181 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) {
182 goto FOUND_CTL;
183 }
184 }
185 }
186FOUND_CTL:
187 if (likely(*buf == '\015')) {
188 ++buf;
189 EXPECT_CHAR('\012');
190 *token_len = buf - 2 - token_start;
191 } else if (*buf == '\012') {
192 *token_len = buf - token_start;
193 ++buf;
194 } else {
195 *ret = -1;
196 return NULL;
197 }
198 *token = token_start;
199
200 return buf;
201}
202
203static const char *is_complete(const char *buf, const char *buf_end, size_t last_len, int *ret)
204{
205 int ret_cnt = 0;
206 buf = last_len < 3 ? buf : buf + last_len - 3;
207
208 while (1) {
209 CHECK_EOF();
210 if (*buf == '\015') {
211 ++buf;
212 CHECK_EOF();
213 EXPECT_CHAR('\012');
214 ++ret_cnt;
215 } else if (*buf == '\012') {
216 ++buf;
217 ++ret_cnt;
218 } else {
219 ++buf;
220 ret_cnt = 0;
221 }
222 if (ret_cnt == 2) {
223 return buf;
224 }
225 }
226
227 *ret = -2;
228 return NULL;
229}
230
231#define PARSE_INT(valp_, mul_) \
232 if (*buf < '0' || '9' < *buf) { \
233 buf++; \
234 *ret = -1; \
235 return NULL; \
236 } \
237 *(valp_) = (mul_) * (*buf++ - '0');
238
239#define PARSE_INT_3(valp_) \
240 do { \
241 int res_ = 0; \
242 PARSE_INT(&res_, 100) \
243 *valp_ = res_; \
244 PARSE_INT(&res_, 10) \
245 *valp_ += res_; \
246 PARSE_INT(&res_, 1) \
247 *valp_ += res_; \
248 } while (0)
249
250/* returned pointer is always within [buf, buf_end), or null */
251static const char *parse_http_version(const char *buf, const char *buf_end, int *minor_version, int *ret)
252{
253 /* we want at least [HTTP/1.<two chars>] to try to parse */
254 if (buf_end - buf < 9) {
255 *ret = -2;
256 return NULL;
257 }
258 EXPECT_CHAR_NO_CHECK('H');
259 EXPECT_CHAR_NO_CHECK('T');
260 EXPECT_CHAR_NO_CHECK('T');
261 EXPECT_CHAR_NO_CHECK('P');
262 EXPECT_CHAR_NO_CHECK('/');
263 EXPECT_CHAR_NO_CHECK('1');
264 EXPECT_CHAR_NO_CHECK('.');
265 PARSE_INT(minor_version, 1);
266 return buf;
267}
268
269static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers, size_t *num_headers,
270 size_t max_headers, int *ret)
271{
272 for (;; ++*num_headers) {
273 CHECK_EOF();
274 if (*buf == '\015') {
275 ++buf;
276 EXPECT_CHAR('\012');
277 break;
278 } else if (*buf == '\012') {
279 ++buf;
280 break;
281 }
282 if (*num_headers == max_headers) {
283 *ret = -1;
284 return NULL;
285 }
286 if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) {
287 /* parsing name, but do not discard SP before colon, see
288 * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */
289 headers[*num_headers].name = buf;
290 static const char ALIGNED(16) ranges1[] = "\x00 " /* control chars and up to SP */
291 "\"\"" /* 0x22 */
292 "()" /* 0x28,0x29 */
293 ",," /* 0x2c */
294 "//" /* 0x2f */
295 ":@" /* 0x3a-0x40 */
296 "[]" /* 0x5b-0x5d */
297 "{\377"; /* 0x7b-0xff */
298 int found;
299 buf = findchar_fast(buf, buf_end, ranges1, sizeof(ranges1) - 1, &found);
300 if (!found) {
301 CHECK_EOF();
302 }
303 while (1) {
304 if (*buf == ':') {
305 break;
306 } else if (!token_char_map[(unsigned char)*buf]) {
307 *ret = -1;
308 return NULL;
309 }
310 ++buf;
311 CHECK_EOF();
312 }
313 if ((headers[*num_headers].name_len = buf - headers[*num_headers].name) == 0) {
314 *ret = -1;
315 return NULL;
316 }
317 ++buf;
318 for (;; ++buf) {
319 CHECK_EOF();
320 if (!(*buf == ' ' || *buf == '\t')) {
321 break;
322 }
323 }
324 } else {
325 headers[*num_headers].name = NULL;
326 headers[*num_headers].name_len = 0;
327 }
328 if ((buf = get_token_to_eol(buf, buf_end, &headers[*num_headers].value, &headers[*num_headers].value_len, ret)) == NULL) {
329 return NULL;
330 }
331 }
332 return buf;
333}
334
335static const char *parse_request(const char *buf, const char *buf_end, const char **method, size_t *method_len, const char **path,
336 size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers,
337 size_t max_headers, int *ret)
338{
339 /* skip first empty line (some clients add CRLF after POST content) */
340 CHECK_EOF();
341 if (*buf == '\015') {
342 ++buf;
343 EXPECT_CHAR('\012');
344 } else if (*buf == '\012') {
345 ++buf;
346 }
347
348 /* parse request line */
349 ADVANCE_TOKEN(*method, *method_len);
350 ++buf;
351 ADVANCE_TOKEN(*path, *path_len);
352 ++buf;
353 if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {
354 return NULL;
355 }
356 if (*buf == '\015') {
357 ++buf;
358 EXPECT_CHAR('\012');
359 } else if (*buf == '\012') {
360 ++buf;
361 } else {
362 *ret = -1;
363 return NULL;
364 }
365
366 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
367}
368
369int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len, const char **path,
370 size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len)
371{
372 const char *buf = buf_start, *buf_end = buf_start + len;
373 size_t max_headers = *num_headers;
374 int r;
375
376 *method = NULL;
377 *method_len = 0;
378 *path = NULL;
379 *path_len = 0;
380 *minor_version = -1;
381 *num_headers = 0;
382
383 /* if last_len != 0, check if the request is complete (a fast countermeasure
384 againt slowloris */
385 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
386 return r;
387 }
388
389 if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, minor_version, headers, num_headers, max_headers,
390 &r)) == NULL) {
391 return r;
392 }
393
394 return (int)(buf - buf_start);
395}
396
397static const char *parse_response(const char *buf, const char *buf_end, int *minor_version, int *status, const char **msg,
398 size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t max_headers, int *ret)
399{
400 /* parse "HTTP/1.x" */
401 if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {
402 return NULL;
403 }
404 /* skip space */
405 if (*buf++ != ' ') {
406 *ret = -1;
407 return NULL;
408 }
409 /* parse status code, we want at least [:digit:][:digit:][:digit:]<other char> to try to parse */
410 if (buf_end - buf < 4) {
411 *ret = -2;
412 return NULL;
413 }
414 PARSE_INT_3(status);
415
416 /* skip space */
417 if (*buf++ != ' ') {
418 *ret = -1;
419 return NULL;
420 }
421 /* get message */
422 if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) {
423 return NULL;
424 }
425
426 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
427}
428
429int phr_parse_response(const char *buf_start, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len,
430 struct phr_header *headers, size_t *num_headers, size_t last_len)
431{
432 const char *buf = buf_start, *buf_end = buf + len;
433 size_t max_headers = *num_headers;
434 int r;
435
436 *minor_version = -1;
437 *status = 0;
438 *msg = NULL;
439 *msg_len = 0;
440 *num_headers = 0;
441
442 /* if last_len != 0, check if the response is complete (a fast countermeasure
443 against slowloris */
444 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
445 return r;
446 }
447
448 if ((buf = parse_response(buf, buf_end, minor_version, status, msg, msg_len, headers, num_headers, max_headers, &r)) == NULL) {
449 return r;
450 }
451
452 return (int)(buf - buf_start);
453}
454
455int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len)
456{
457 const char *buf = buf_start, *buf_end = buf + len;
458 size_t max_headers = *num_headers;
459 int r;
460
461 *num_headers = 0;
462
463 /* if last_len != 0, check if the response is complete (a fast countermeasure
464 against slowloris */
465 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
466 return r;
467 }
468
469 if ((buf = parse_headers(buf, buf_end, headers, num_headers, max_headers, &r)) == NULL) {
470 return r;
471 }
472
473 return (int)(buf - buf_start);
474}
475
476enum {
477 CHUNKED_IN_CHUNK_SIZE,
478 CHUNKED_IN_CHUNK_EXT,
479 CHUNKED_IN_CHUNK_DATA,
480 CHUNKED_IN_CHUNK_CRLF,
481 CHUNKED_IN_TRAILERS_LINE_HEAD,
482 CHUNKED_IN_TRAILERS_LINE_MIDDLE
483};
484
485static int decode_hex(int ch)
486{
487 if ('0' <= ch && ch <= '9') {
488 return ch - '0';
489 } else if ('A' <= ch && ch <= 'F') {
490 return ch - 'A' + 0xa;
491 } else if ('a' <= ch && ch <= 'f') {
492 return ch - 'a' + 0xa;
493 } else {
494 return -1;
495 }
496}
497
498ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *_bufsz)
499{
500 size_t dst = 0, src = 0, bufsz = *_bufsz;
501 ssize_t ret = -2; /* incomplete */
502
503 while (1) {
504 switch (decoder->_state) {
505 case CHUNKED_IN_CHUNK_SIZE:
506 for (;; ++src) {
507 int v;
508 if (src == bufsz)
509 goto Exit;
510 if ((v = decode_hex(buf[src])) == -1) {
511 if (decoder->_hex_count == 0) {
512 ret = -1;
513 goto Exit;
514 }
515 break;
516 }
517 if (decoder->_hex_count == sizeof(size_t) * 2) {
518 ret = -1;
519 goto Exit;
520 }
521 decoder->bytes_left_in_chunk = decoder->bytes_left_in_chunk * 16 + v;
522 ++decoder->_hex_count;
523 }
524 decoder->_hex_count = 0;
525 decoder->_state = CHUNKED_IN_CHUNK_EXT;
526 /* fallthru */
527 case CHUNKED_IN_CHUNK_EXT:
528 /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */
529 for (;; ++src) {
530 if (src == bufsz)
531 goto Exit;
532 if (buf[src] == '\012')
533 break;
534 }
535 ++src;
536 if (decoder->bytes_left_in_chunk == 0) {
537 if (decoder->consume_trailer) {
538 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
539 break;
540 } else {
541 goto Complete;
542 }
543 }
544 decoder->_state = CHUNKED_IN_CHUNK_DATA;
545 /* fallthru */
546 case CHUNKED_IN_CHUNK_DATA: {
547 size_t avail = bufsz - src;
548 if (avail < decoder->bytes_left_in_chunk) {
549 if (dst != src)
550 memmove(buf + dst, buf + src, avail);
551 src += avail;
552 dst += avail;
553 decoder->bytes_left_in_chunk -= avail;
554 goto Exit;
555 }
556 if (dst != src)
557 memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk);
558 src += decoder->bytes_left_in_chunk;
559 dst += decoder->bytes_left_in_chunk;
560 decoder->bytes_left_in_chunk = 0;
561 decoder->_state = CHUNKED_IN_CHUNK_CRLF;
562 }
563 /* fallthru */
564 case CHUNKED_IN_CHUNK_CRLF:
565 for (;; ++src) {
566 if (src == bufsz)
567 goto Exit;
568 if (buf[src] != '\015')
569 break;
570 }
571 if (buf[src] != '\012') {
572 ret = -1;
573 goto Exit;
574 }
575 ++src;
576 decoder->_state = CHUNKED_IN_CHUNK_SIZE;
577 break;
578 case CHUNKED_IN_TRAILERS_LINE_HEAD:
579 for (;; ++src) {
580 if (src == bufsz)
581 goto Exit;
582 if (buf[src] != '\015')
583 break;
584 }
585 if (buf[src++] == '\012')
586 goto Complete;
587 decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE;
588 /* fallthru */
589 case CHUNKED_IN_TRAILERS_LINE_MIDDLE:
590 for (;; ++src) {
591 if (src == bufsz)
592 goto Exit;
593 if (buf[src] == '\012')
594 break;
595 }
596 ++src;
597 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
598 break;
599 default:
600 assert(!"decoder is corrupt");
601 }
602 }
603
604Complete:
605 ret = bufsz - src;
606Exit:
607 if (dst != src)
608 memmove(buf + dst, buf + src, bufsz - src);
609 *_bufsz = dst;
610 return ret;
611}
612
613int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder)
614{
615 return decoder->_state == CHUNKED_IN_CHUNK_DATA;
616}
617
618#undef CHECK_EOF
619#undef EXPECT_CHAR
620#undef ADVANCE_TOKEN
diff --git a/plugins/picohttpparser.h b/plugins/picohttpparser.h
new file mode 100644
index 0000000..a8fad71
--- /dev/null
+++ b/plugins/picohttpparser.h
@@ -0,0 +1,89 @@
1/*
2 * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
3 * Shigeo Mitsunari
4 *
5 * The software is licensed under either the MIT License (below) or the Perl
6 * license.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to
10 * deal in the Software without restriction, including without limitation the
11 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12 * sell copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 * IN THE SOFTWARE.
25 */
26
27#ifndef picohttpparser_h
28#define picohttpparser_h
29
30#include <sys/types.h>
31
32#ifdef _MSC_VER
33#define ssize_t intptr_t
34#endif
35
36/* $Id: 67fd3ee74103ada60258d8a16e868f483abcca87 $ */
37
38#ifdef __cplusplus
39extern "C" {
40#endif
41
42/* contains name and value of a header (name == NULL if is a continuing line
43 * of a multiline header */
44struct phr_header {
45 const char *name;
46 size_t name_len;
47 const char *value;
48 size_t value_len;
49};
50
51/* returns number of bytes consumed if successful, -2 if request is partial,
52 * -1 if failed */
53int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len,
54 int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len);
55
56/* ditto */
57int phr_parse_response(const char *_buf, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len,
58 struct phr_header *headers, size_t *num_headers, size_t last_len);
59
60/* ditto */
61int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len);
62
63/* should be zero-filled before start */
64struct phr_chunked_decoder {
65 size_t bytes_left_in_chunk; /* number of bytes left in current chunk */
66 char consume_trailer; /* if trailing headers should be consumed */
67 char _hex_count;
68 char _state;
69};
70
71/* the function rewrites the buffer given as (buf, bufsz) removing the chunked-
72 * encoding headers. When the function returns without an error, bufsz is
73 * updated to the length of the decoded data available. Applications should
74 * repeatedly call the function while it returns -2 (incomplete) every time
75 * supplying newly arrived data. If the end of the chunked-encoded data is
76 * found, the function returns a non-negative number indicating the number of
77 * octets left undecoded at the tail of the supplied buffer. Returns -1 on
78 * error.
79 */
80ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *bufsz);
81
82/* returns if the chunked decoder is in middle of chunked data */
83int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder);
84
85#ifdef __cplusplus
86}
87#endif
88
89#endif
diff --git a/plugins/sslutils.c b/plugins/sslutils.c
index e38947e..14f6579 100644
--- a/plugins/sslutils.c
+++ b/plugins/sslutils.c
@@ -1,29 +1,29 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2*
3* Monitoring Plugins SSL utilities 3* Monitoring Plugins SSL utilities
4* 4*
5* License: GPL 5* License: GPL
6* Copyright (c) 2005-2010 Monitoring Plugins Development Team 6* Copyright (c) 2005-2010 Monitoring Plugins Development Team
7* 7*
8* Description: 8* Description:
9* 9*
10* This file contains common functions for plugins that require SSL. 10* This file contains common functions for plugins that require SSL.
11* 11*
12* 12*
13* This program is free software: you can redistribute it and/or modify 13* This program is free software: you can redistribute it and/or modify
14* it under the terms of the GNU General Public License as published by 14* it under the terms of the GNU General Public License as published by
15* the Free Software Foundation, either version 3 of the License, or 15* the Free Software Foundation, either version 3 of the License, or
16* (at your option) any later version. 16* (at your option) any later version.
17* 17*
18* This program is distributed in the hope that it will be useful, 18* This program is distributed in the hope that it will be useful,
19* but WITHOUT ANY WARRANTY; without even the implied warranty of 19* but WITHOUT ANY WARRANTY; without even the implied warranty of
20* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21* GNU General Public License for more details. 21* GNU General Public License for more details.
22* 22*
23* You should have received a copy of the GNU General Public License 23* You should have received a copy of the GNU General Public License
24* along with this program. If not, see <http://www.gnu.org/licenses/>. 24* along with this program. If not, see <http://www.gnu.org/licenses/>.
25* 25*
26* 26*
27*****************************************************************************/ 27*****************************************************************************/
28 28
29#define MAX_CN_LENGTH 256 29#define MAX_CN_LENGTH 256
@@ -193,12 +193,22 @@ int np_net_ssl_read(void *buf, int num) {
193 193
194int np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit){ 194int np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit){
195# ifdef USE_OPENSSL 195# ifdef USE_OPENSSL
196 X509 *certificate=NULL; 196 X509 *certificate = NULL;
197 certificate=SSL_get_peer_certificate(s);
198 return(np_net_ssl_check_certificate(certificate, days_till_exp_warn, days_till_exp_crit));
199# else /* ifndef USE_OPENSSL */
200 printf("%s\n", _("WARNING - Plugin does not support checking certificates."));
201 return STATE_WARNING;
202# endif /* USE_OPENSSL */
203}
204
205int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit){
206# ifdef USE_OPENSSL
197 X509_NAME *subj=NULL; 207 X509_NAME *subj=NULL;
198 char timestamp[50] = ""; 208 char timestamp[50] = "";
199 char cn[MAX_CN_LENGTH]= ""; 209 char cn[MAX_CN_LENGTH]= "";
200 char *tz; 210 char *tz;
201 211
202 int cnlen =-1; 212 int cnlen =-1;
203 int status=STATE_UNKNOWN; 213 int status=STATE_UNKNOWN;
204 214
@@ -210,7 +220,6 @@ int np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit){
210 int time_remaining; 220 int time_remaining;
211 time_t tm_t; 221 time_t tm_t;
212 222
213 certificate=SSL_get_peer_certificate(s);
214 if (!certificate) { 223 if (!certificate) {
215 printf("%s\n",_("CRITICAL - Cannot retrieve server certificate.")); 224 printf("%s\n",_("CRITICAL - Cannot retrieve server certificate."));
216 return STATE_CRITICAL; 225 return STATE_CRITICAL;
diff --git a/plugins/t/check_curl.t b/plugins/t/check_curl.t
new file mode 100644
index 0000000..e67fafb
--- /dev/null
+++ b/plugins/t/check_curl.t
@@ -0,0 +1,204 @@
1#! /usr/bin/perl -w -I ..
2#
3# HyperText Transfer Protocol (HTTP) Test via check_http
4#
5#
6
7use strict;
8use Test::More;
9use POSIX qw/mktime strftime/;
10use NPTest;
11
12plan tests => 49;
13
14my $successOutput = '/OK.*HTTP.*second/';
15
16my $res;
17my $plugin = 'check_http';
18$plugin = 'check_curl' if $0 =~ m/check_curl/mx;
19
20my $host_tcp_http = getTestParameter( "NP_HOST_TCP_HTTP",
21 "A host providing the HTTP Service (a web server)",
22 "localhost" );
23
24my $host_tls_http = getTestParameter( "host_tls_http", "NP_HOST_TLS_HTTP", "localhost",
25 "A host providing the HTTPS Service (a tls web server)" );
26
27my $host_tls_cert = getTestParameter( "host_tls_cert", "NP_HOST_TLS_CERT", "localhost",
28 "the common name of the certificate." );
29
30
31my $host_nonresponsive = getTestParameter( "NP_HOST_NONRESPONSIVE",
32 "The hostname of system not responsive to network requests",
33 "10.0.0.1" );
34
35my $hostname_invalid = getTestParameter( "NP_HOSTNAME_INVALID",
36 "An invalid (not known to DNS) hostname",
37 "nosuchhost");
38
39my $internet_access = getTestParameter( "NP_INTERNET_ACCESS",
40 "Is this system directly connected to the internet?",
41 "yes");
42
43my $host_tcp_http2 = getTestParameter( "NP_HOST_TCP_HTTP2",
44 "A host providing an index page containing the string 'monitoring'",
45 "test.monitoring-plugins.org" );
46
47my $faketime = -x '/usr/bin/faketime' ? 1 : 0;
48
49
50$res = NPTest->testCmd(
51 "./$plugin $host_tcp_http -wt 300 -ct 600"
52 );
53cmp_ok( $res->return_code, '==', 0, "Webserver $host_tcp_http responded" );
54like( $res->output, $successOutput, "Output OK" );
55
56$res = NPTest->testCmd(
57 "./$plugin $host_tcp_http -wt 300 -ct 600 -v -v -v -k 'bob:there' -k 'carl:frown'"
58 );
59like( $res->output, '/bob:there\r\ncarl:frown\r\n/', "Got headers with multiple -k options" );
60
61$res = NPTest->testCmd(
62 "./$plugin $host_nonresponsive -wt 1 -ct 2 -t 3"
63 );
64cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" );
65# was CRITICAL only, but both check_curl and check_http print HTTP CRITICAL (puzzle?!)
66cmp_ok( $res->output, 'eq', "HTTP CRITICAL - Invalid HTTP response received from host on port 80: cURL returned 28 - Timeout was reached", "Output OK");
67
68$res = NPTest->testCmd(
69 "./$plugin $hostname_invalid -wt 1 -ct 2"
70 );
71cmp_ok( $res->return_code, '==', 2, "Webserver $hostname_invalid not valid" );
72# The first part of the message comes from the OS catalogue, so cannot check this.
73# On Debian, it is Name or service not known, on Darwin, it is No address associated with nodename
74# Is also possible to get a socket timeout if DNS is not responding fast enough
75# cURL gives us consistent strings from it's own 'lib/strerror.c'
76like( $res->output, "/cURL returned 6 - Couldn't resolve host name/", "Output OK");
77
78# host header checks
79$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http");
80like( $res->output, '/^Host: '.$host_tcp_http.'\s*$/ms', "Host Header OK" );
81
82$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http -p 80");
83like( $res->output, '/^Host: '.$host_tcp_http.'\s*$/ms', "Host Header OK" );
84
85$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http:8080 -p 80");
86like( $res->output, '/^Host: '.$host_tcp_http.':8080\s*$/ms', "Host Header OK" );
87
88$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http:8080 -p 80");
89like( $res->output, '/^Host: '.$host_tcp_http.':8080\s*$/ms', "Host Header OK" );
90
91SKIP: {
92 skip "No internet access", 3 if $internet_access eq "no";
93
94 $res = NPTest->testCmd("./$plugin -v -H $host_tls_http -S");
95 like( $res->output, '/^Host: '.$host_tls_http.'\s*$/ms', "Host Header OK" );
96
97 $res = NPTest->testCmd("./$plugin -v -H $host_tls_http:8080 -S -p 443");
98 like( $res->output, '/^Host: '.$host_tls_http.':8080\s*$/ms', "Host Header OK" );
99
100 $res = NPTest->testCmd("./$plugin -v -H $host_tls_http:443 -S -p 443");
101 like( $res->output, '/^Host: '.$host_tls_http.'\s*$/ms', "Host Header OK" );
102};
103
104SKIP: {
105 skip "No host serving monitoring in index file", 7 unless $host_tcp_http2;
106
107 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'monitoring'" );
108 cmp_ok( $res->return_code, "==", 0, "Got a reference to 'monitoring'");
109
110 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing'" );
111 cmp_ok( $res->return_code, "==", 2, "Not got 'mONiTORing'");
112 like ( $res->output, "/pattern not found/", "Error message says 'pattern not found'");
113
114 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -R 'mONiTORing'" );
115 cmp_ok( $res->return_code, "==", 0, "But case insensitive doesn't mind 'mONiTORing'");
116
117 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'monitoring' --invert-regex" );
118 cmp_ok( $res->return_code, "==", 2, "Invert results work when found");
119 like ( $res->output, "/pattern found/", "Error message says 'pattern found'");
120
121 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing' --invert-regex" );
122 cmp_ok( $res->return_code, "==", 0, "And also when not found");
123}
124SKIP: {
125 skip "No internet access", 16 if $internet_access eq "no";
126
127 $res = NPTest->testCmd(
128 "./$plugin --ssl $host_tls_http"
129 );
130 cmp_ok( $res->return_code, '==', 0, "Can read https for $host_tls_http" );
131
132 $res = NPTest->testCmd( "./$plugin -C 1 --ssl $host_tls_http" );
133 cmp_ok( $res->return_code, '==', 0, "Checking certificate for $host_tls_http");
134 like ( $res->output, "/Certificate '$host_tls_cert' will expire on/", "Output OK" );
135 my $saved_cert_output = $res->output;
136
137 $res = NPTest->testCmd( "./$plugin -C 8000,1 --ssl $host_tls_http" );
138 cmp_ok( $res->return_code, '==', 1, "Checking certificate for $host_tls_http");
139 like ( $res->output, qr/WARNING - Certificate '$host_tls_cert' expires in \d+ day/, "Output Warning" );
140
141 $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" );
142 is( $res->return_code, 0, "Old syntax for cert checking okay" );
143 is( $res->output, $saved_cert_output, "Same output as new syntax" );
144
145 $res = NPTest->testCmd( "./$plugin -H $host_tls_http -C 1" );
146 is( $res->return_code, 0, "Updated syntax for cert checking okay" );
147 is( $res->output, $saved_cert_output, "Same output as new syntax" );
148
149 $res = NPTest->testCmd( "./$plugin -C 1 $host_tls_http" );
150 cmp_ok( $res->output, 'eq', $saved_cert_output, "--ssl option automatically added");
151
152 $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" );
153 cmp_ok( $res->output, 'eq', $saved_cert_output, "Old syntax for cert checking still works");
154
155 # run some certificate checks with faketime
156 SKIP: {
157 skip "No faketime binary found", 12 if !$faketime;
158 $res = NPTest->testCmd("LC_TIME=C TZ=UTC ./$plugin -C 1 $host_tls_http");
159 like($res->output, qr/OK - Certificate '$host_tls_cert' will expire on/, "Catch cert output");
160 is( $res->return_code, 0, "Catch cert output exit code" );
161 my($mon,$day,$hour,$min,$sec,$year) = ($res->output =~ /(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)/);
162 if(!defined $year) {
163 die("parsing date failed from: ".$res->output);
164 }
165 my $months = {'Jan' => 0, 'Feb' => 1, 'Mar' => 2, 'Apr' => 3, 'May' => 4, 'Jun' => 5, 'Jul' => 6, 'Aug' => 7, 'Sep' => 8, 'Oct' => 9, 'Nov' => 10, 'Dec' => 11};
166 my $ts = mktime($sec, $min, $hour, $day, $months->{$mon}, $year-1900);
167 my $time = strftime("%Y-%m-%d %H:%M:%S", localtime($ts));
168 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./$plugin -C 1 $host_tls_http");
169 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' just expired/, "Output on expire date");
170 is( $res->return_code, 2, "Output on expire date" );
171
172 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-1))."' ./$plugin -C 1 $host_tls_http");
173 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 0 minutes/, "cert expires in 1 second output");
174 is( $res->return_code, 2, "cert expires in 1 second exit code" );
175
176 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-120))."' ./$plugin -C 1 $host_tls_http");
177 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 minutes/, "cert expires in 2 minutes output");
178 is( $res->return_code, 2, "cert expires in 2 minutes exit code" );
179
180 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-7200))."' ./$plugin -C 1 $host_tls_http");
181 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 hours/, "cert expires in 2 hours output");
182 is( $res->return_code, 2, "cert expires in 2 hours exit code" );
183
184 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./$plugin -C 1 $host_tls_http");
185 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expired on/, "Certificate expired output");
186 is( $res->return_code, 2, "Certificate expired exit code" );
187 };
188
189 $res = NPTest->testCmd( "./$plugin --ssl $host_tls_http -E" );
190 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' );
191 like ( $res->output, '/time_ssl=[\d\.]+/', 'Extended Performance Data SSL Output OK' );
192
193 $res = NPTest->testCmd(
194 "./$plugin --ssl -H www.e-paycobalt.com"
195 );
196 cmp_ok( $res->return_code, "==", 0, "Can read https for www.e-paycobalt.com (uses AES certificate)" );
197
198
199 $res = NPTest->testCmd( "./$plugin -H www.mozilla.com -u /firefox -f follow" );
200 is( $res->return_code, 0, "Redirection based on location is okay");
201
202 $res = NPTest->testCmd( "./$plugin -H www.mozilla.com --extended-perfdata" );
203 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' );
204}
diff --git a/plugins/t/check_http.t b/plugins/t/check_http.t
index 8bd484a..281fa36 100644
--- a/plugins/t/check_http.t
+++ b/plugins/t/check_http.t
@@ -14,6 +14,8 @@ plan tests => 49;
14my $successOutput = '/OK.*HTTP.*second/'; 14my $successOutput = '/OK.*HTTP.*second/';
15 15
16my $res; 16my $res;
17my $plugin = 'check_http';
18$plugin = 'check_curl' if $0 =~ m/check_curl/mx;
17 19
18my $host_tcp_http = getTestParameter( "NP_HOST_TCP_HTTP", 20my $host_tcp_http = getTestParameter( "NP_HOST_TCP_HTTP",
19 "A host providing the HTTP Service (a web server)", 21 "A host providing the HTTP Service (a web server)",
@@ -46,24 +48,24 @@ my $faketime = -x '/usr/bin/faketime' ? 1 : 0;
46 48
47 49
48$res = NPTest->testCmd( 50$res = NPTest->testCmd(
49 "./check_http $host_tcp_http -wt 300 -ct 600" 51 "./$plugin $host_tcp_http -wt 300 -ct 600"
50 ); 52 );
51cmp_ok( $res->return_code, '==', 0, "Webserver $host_tcp_http responded" ); 53cmp_ok( $res->return_code, '==', 0, "Webserver $host_tcp_http responded" );
52like( $res->output, $successOutput, "Output OK" ); 54like( $res->output, $successOutput, "Output OK" );
53 55
54$res = NPTest->testCmd( 56$res = NPTest->testCmd(
55 "./check_http $host_tcp_http -wt 300 -ct 600 -v -v -v -k 'bob:there' -k 'carl:frown'" 57 "./$plugin $host_tcp_http -wt 300 -ct 600 -v -v -v -k 'bob:there' -k 'carl:frown'"
56 ); 58 );
57like( $res->output, '/bob:there\r\ncarl:frown\r\n/', "Got headers with multiple -k options" ); 59like( $res->output, '/bob:there\r\ncarl:frown\r\n/', "Got headers with multiple -k options" );
58 60
59$res = NPTest->testCmd( 61$res = NPTest->testCmd(
60 "./check_http $host_nonresponsive -wt 1 -ct 2 -t 3" 62 "./$plugin $host_nonresponsive -wt 1 -ct 2 -t 3"
61 ); 63 );
62cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" ); 64cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" );
63cmp_ok( $res->output, 'eq', "CRITICAL - Socket timeout after 3 seconds", "Output OK"); 65cmp_ok( $res->output, 'eq', "CRITICAL - Socket timeout after 3 seconds", "Output OK");
64 66
65$res = NPTest->testCmd( 67$res = NPTest->testCmd(
66 "./check_http $hostname_invalid -wt 1 -ct 2" 68 "./$plugin $hostname_invalid -wt 1 -ct 2"
67 ); 69 );
68cmp_ok( $res->return_code, '==', 2, "Webserver $hostname_invalid not valid" ); 70cmp_ok( $res->return_code, '==', 2, "Webserver $hostname_invalid not valid" );
69# The first part of the message comes from the OS catalogue, so cannot check this. 71# The first part of the message comes from the OS catalogue, so cannot check this.
@@ -72,86 +74,86 @@ cmp_ok( $res->return_code, '==', 2, "Webserver $hostname_invalid not valid" );
72like( $res->output, "/Unable to open TCP socket|Socket timeout after/", "Output OK"); 74like( $res->output, "/Unable to open TCP socket|Socket timeout after/", "Output OK");
73 75
74# host header checks 76# host header checks
75$res = NPTest->testCmd("./check_http -v -H $host_tcp_http"); 77$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http");
76like( $res->output, '/^Host: '.$host_tcp_http.'\s*$/ms', "Host Header OK" ); 78like( $res->output, '/^Host: '.$host_tcp_http.'\s*$/ms', "Host Header OK" );
77 79
78$res = NPTest->testCmd("./check_http -v -H $host_tcp_http -p 80"); 80$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http -p 80");
79like( $res->output, '/^Host: '.$host_tcp_http.'\s*$/ms', "Host Header OK" ); 81like( $res->output, '/^Host: '.$host_tcp_http.'\s*$/ms', "Host Header OK" );
80 82
81$res = NPTest->testCmd("./check_http -v -H $host_tcp_http:8080 -p 80"); 83$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http:8080 -p 80");
82like( $res->output, '/^Host: '.$host_tcp_http.':8080\s*$/ms', "Host Header OK" ); 84like( $res->output, '/^Host: '.$host_tcp_http.':8080\s*$/ms', "Host Header OK" );
83 85
84$res = NPTest->testCmd("./check_http -v -H $host_tcp_http:8080 -p 80"); 86$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http:8080 -p 80");
85like( $res->output, '/^Host: '.$host_tcp_http.':8080\s*$/ms', "Host Header OK" ); 87like( $res->output, '/^Host: '.$host_tcp_http.':8080\s*$/ms', "Host Header OK" );
86 88
87SKIP: { 89SKIP: {
88 skip "No internet access", 3 if $internet_access eq "no"; 90 skip "No internet access", 3 if $internet_access eq "no";
89 91
90 $res = NPTest->testCmd("./check_http -v -H $host_tls_http -S"); 92 $res = NPTest->testCmd("./$plugin -v -H $host_tls_http -S");
91 like( $res->output, '/^Host: '.$host_tls_http.'\s*$/ms', "Host Header OK" ); 93 like( $res->output, '/^Host: '.$host_tls_http.'\s*$/ms', "Host Header OK" );
92 94
93 $res = NPTest->testCmd("./check_http -v -H $host_tls_http:8080 -S -p 443"); 95 $res = NPTest->testCmd("./$plugin -v -H $host_tls_http:8080 -S -p 443");
94 like( $res->output, '/^Host: '.$host_tls_http.':8080\s*$/ms', "Host Header OK" ); 96 like( $res->output, '/^Host: '.$host_tls_http.':8080\s*$/ms', "Host Header OK" );
95 97
96 $res = NPTest->testCmd("./check_http -v -H $host_tls_http:443 -S -p 443"); 98 $res = NPTest->testCmd("./$plugin -v -H $host_tls_http:443 -S -p 443");
97 like( $res->output, '/^Host: '.$host_tls_http.'\s*$/ms', "Host Header OK" ); 99 like( $res->output, '/^Host: '.$host_tls_http.'\s*$/ms', "Host Header OK" );
98}; 100};
99 101
100SKIP: { 102SKIP: {
101 skip "No host serving monitoring in index file", 7 unless $host_tcp_http2; 103 skip "No host serving monitoring in index file", 7 unless $host_tcp_http2;
102 104
103 $res = NPTest->testCmd( "./check_http -H $host_tcp_http2 -r 'monitoring'" ); 105 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'monitoring'" );
104 cmp_ok( $res->return_code, "==", 0, "Got a reference to 'monitoring'"); 106 cmp_ok( $res->return_code, "==", 0, "Got a reference to 'monitoring'");
105 107
106 $res = NPTest->testCmd( "./check_http -H $host_tcp_http2 -r 'mONiTORing'" ); 108 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing'" );
107 cmp_ok( $res->return_code, "==", 2, "Not got 'mONiTORing'"); 109 cmp_ok( $res->return_code, "==", 2, "Not got 'mONiTORing'");
108 like ( $res->output, "/pattern not found/", "Error message says 'pattern not found'"); 110 like ( $res->output, "/pattern not found/", "Error message says 'pattern not found'");
109 111
110 $res = NPTest->testCmd( "./check_http -H $host_tcp_http2 -R 'mONiTORing'" ); 112 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -R 'mONiTORing'" );
111 cmp_ok( $res->return_code, "==", 0, "But case insensitive doesn't mind 'mONiTORing'"); 113 cmp_ok( $res->return_code, "==", 0, "But case insensitive doesn't mind 'mONiTORing'");
112 114
113 $res = NPTest->testCmd( "./check_http -H $host_tcp_http2 -r 'monitoring' --invert-regex" ); 115 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'monitoring' --invert-regex" );
114 cmp_ok( $res->return_code, "==", 2, "Invert results work when found"); 116 cmp_ok( $res->return_code, "==", 2, "Invert results work when found");
115 like ( $res->output, "/pattern found/", "Error message says 'pattern found'"); 117 like ( $res->output, "/pattern found/", "Error message says 'pattern found'");
116 118
117 $res = NPTest->testCmd( "./check_http -H $host_tcp_http2 -r 'mONiTORing' --invert-regex" ); 119 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing' --invert-regex" );
118 cmp_ok( $res->return_code, "==", 0, "And also when not found"); 120 cmp_ok( $res->return_code, "==", 0, "And also when not found");
119} 121}
120SKIP: { 122SKIP: {
121 skip "No internet access", 16 if $internet_access eq "no"; 123 skip "No internet access", 16 if $internet_access eq "no";
122 124
123 $res = NPTest->testCmd( 125 $res = NPTest->testCmd(
124 "./check_http --ssl $host_tls_http" 126 "./$plugin --ssl $host_tls_http"
125 ); 127 );
126 cmp_ok( $res->return_code, '==', 0, "Can read https for $host_tls_http" ); 128 cmp_ok( $res->return_code, '==', 0, "Can read https for $host_tls_http" );
127 129
128 $res = NPTest->testCmd( "./check_http -C 1 --ssl $host_tls_http" ); 130 $res = NPTest->testCmd( "./$plugin -C 1 --ssl $host_tls_http" );
129 cmp_ok( $res->return_code, '==', 0, "Checking certificate for $host_tls_http"); 131 cmp_ok( $res->return_code, '==', 0, "Checking certificate for $host_tls_http");
130 like ( $res->output, "/Certificate '$host_tls_cert' will expire on/", "Output OK" ); 132 like ( $res->output, "/Certificate '$host_tls_cert' will expire on/", "Output OK" );
131 my $saved_cert_output = $res->output; 133 my $saved_cert_output = $res->output;
132 134
133 $res = NPTest->testCmd( "./check_http -C 8000,1 --ssl $host_tls_http" ); 135 $res = NPTest->testCmd( "./$plugin -C 8000,1 --ssl $host_tls_http" );
134 cmp_ok( $res->return_code, '==', 1, "Checking certificate for $host_tls_http"); 136 cmp_ok( $res->return_code, '==', 1, "Checking certificate for $host_tls_http");
135 like ( $res->output, qr/WARNING - Certificate '$host_tls_cert' expires in \d+ day/, "Output Warning" ); 137 like ( $res->output, qr/WARNING - Certificate '$host_tls_cert' expires in \d+ day/, "Output Warning" );
136 138
137 $res = NPTest->testCmd( "./check_http $host_tls_http -C 1" ); 139 $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" );
138 is( $res->return_code, 0, "Old syntax for cert checking okay" ); 140 is( $res->return_code, 0, "Old syntax for cert checking okay" );
139 is( $res->output, $saved_cert_output, "Same output as new syntax" ); 141 is( $res->output, $saved_cert_output, "Same output as new syntax" );
140 142
141 $res = NPTest->testCmd( "./check_http -H $host_tls_http -C 1" ); 143 $res = NPTest->testCmd( "./$plugin -H $host_tls_http -C 1" );
142 is( $res->return_code, 0, "Updated syntax for cert checking okay" ); 144 is( $res->return_code, 0, "Updated syntax for cert checking okay" );
143 is( $res->output, $saved_cert_output, "Same output as new syntax" ); 145 is( $res->output, $saved_cert_output, "Same output as new syntax" );
144 146
145 $res = NPTest->testCmd( "./check_http -C 1 $host_tls_http" ); 147 $res = NPTest->testCmd( "./$plugin -C 1 $host_tls_http" );
146 cmp_ok( $res->output, 'eq', $saved_cert_output, "--ssl option automatically added"); 148 cmp_ok( $res->output, 'eq', $saved_cert_output, "--ssl option automatically added");
147 149
148 $res = NPTest->testCmd( "./check_http $host_tls_http -C 1" ); 150 $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" );
149 cmp_ok( $res->output, 'eq', $saved_cert_output, "Old syntax for cert checking still works"); 151 cmp_ok( $res->output, 'eq', $saved_cert_output, "Old syntax for cert checking still works");
150 152
151 # run some certificate checks with faketime 153 # run some certificate checks with faketime
152 SKIP: { 154 SKIP: {
153 skip "No faketime binary found", 12 if !$faketime; 155 skip "No faketime binary found", 12 if !$faketime;
154 $res = NPTest->testCmd("LC_TIME=C TZ=UTC ./check_http -C 1 $host_tls_http"); 156 $res = NPTest->testCmd("LC_TIME=C TZ=UTC ./$plugin -C 1 $host_tls_http");
155 like($res->output, qr/OK - Certificate '$host_tls_cert' will expire on/, "Catch cert output"); 157 like($res->output, qr/OK - Certificate '$host_tls_cert' will expire on/, "Catch cert output");
156 is( $res->return_code, 0, "Catch cert output exit code" ); 158 is( $res->return_code, 0, "Catch cert output exit code" );
157 my($mon,$day,$hour,$min,$sec,$year) = ($res->output =~ /(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)/); 159 my($mon,$day,$hour,$min,$sec,$year) = ($res->output =~ /(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)/);
@@ -161,40 +163,40 @@ SKIP: {
161 my $months = {'Jan' => 0, 'Feb' => 1, 'Mar' => 2, 'Apr' => 3, 'May' => 4, 'Jun' => 5, 'Jul' => 6, 'Aug' => 7, 'Sep' => 8, 'Oct' => 9, 'Nov' => 10, 'Dec' => 11}; 163 my $months = {'Jan' => 0, 'Feb' => 1, 'Mar' => 2, 'Apr' => 3, 'May' => 4, 'Jun' => 5, 'Jul' => 6, 'Aug' => 7, 'Sep' => 8, 'Oct' => 9, 'Nov' => 10, 'Dec' => 11};
162 my $ts = mktime($sec, $min, $hour, $day, $months->{$mon}, $year-1900); 164 my $ts = mktime($sec, $min, $hour, $day, $months->{$mon}, $year-1900);
163 my $time = strftime("%Y-%m-%d %H:%M:%S", localtime($ts)); 165 my $time = strftime("%Y-%m-%d %H:%M:%S", localtime($ts));
164 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./check_http -C 1 $host_tls_http"); 166 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./$plugin -C 1 $host_tls_http");
165 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' just expired/, "Output on expire date"); 167 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' just expired/, "Output on expire date");
166 is( $res->return_code, 2, "Output on expire date" ); 168 is( $res->return_code, 2, "Output on expire date" );
167 169
168 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-1))."' ./check_http -C 1 $host_tls_http"); 170 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-1))."' ./$plugin -C 1 $host_tls_http");
169 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 0 minutes/, "cert expires in 1 second output"); 171 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 0 minutes/, "cert expires in 1 second output");
170 is( $res->return_code, 2, "cert expires in 1 second exit code" ); 172 is( $res->return_code, 2, "cert expires in 1 second exit code" );
171 173
172 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-120))."' ./check_http -C 1 $host_tls_http"); 174 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-120))."' ./$plugin -C 1 $host_tls_http");
173 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 minutes/, "cert expires in 2 minutes output"); 175 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 minutes/, "cert expires in 2 minutes output");
174 is( $res->return_code, 2, "cert expires in 2 minutes exit code" ); 176 is( $res->return_code, 2, "cert expires in 2 minutes exit code" );
175 177
176 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-7200))."' ./check_http -C 1 $host_tls_http"); 178 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-7200))."' ./$plugin -C 1 $host_tls_http");
177 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 hours/, "cert expires in 2 hours output"); 179 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 hours/, "cert expires in 2 hours output");
178 is( $res->return_code, 2, "cert expires in 2 hours exit code" ); 180 is( $res->return_code, 2, "cert expires in 2 hours exit code" );
179 181
180 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_http -C 1 $host_tls_http"); 182 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./$plugin -C 1 $host_tls_http");
181 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expired on/, "Certificate expired output"); 183 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expired on/, "Certificate expired output");
182 is( $res->return_code, 2, "Certificate expired exit code" ); 184 is( $res->return_code, 2, "Certificate expired exit code" );
183 }; 185 };
184 186
185 $res = NPTest->testCmd( "./check_http --ssl $host_tls_http -E" ); 187 $res = NPTest->testCmd( "./$plugin --ssl $host_tls_http -E" );
186 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' ); 188 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' );
187 like ( $res->output, '/time_ssl=[\d\.]+/', 'Extended Performance Data SSL Output OK' ); 189 like ( $res->output, '/time_ssl=[\d\.]+/', 'Extended Performance Data SSL Output OK' );
188 190
189 $res = NPTest->testCmd( 191 $res = NPTest->testCmd(
190 "./check_http --ssl -H www.e-paycobalt.com" 192 "./$plugin --ssl -H www.e-paycobalt.com"
191 ); 193 );
192 cmp_ok( $res->return_code, "==", 0, "Can read https for www.e-paycobalt.com (uses AES certificate)" ); 194 cmp_ok( $res->return_code, "==", 0, "Can read https for www.e-paycobalt.com (uses AES certificate)" );
193 195
194 196
195 $res = NPTest->testCmd( "./check_http -H www.mozilla.com -u /firefox -f follow" ); 197 $res = NPTest->testCmd( "./$plugin -H www.mozilla.com -u /firefox -f follow" );
196 is( $res->return_code, 0, "Redirection based on location is okay"); 198 is( $res->return_code, 0, "Redirection based on location is okay");
197 199
198 $res = NPTest->testCmd( "./check_http -H www.mozilla.com --extended-perfdata" ); 200 $res = NPTest->testCmd( "./$plugin -H www.mozilla.com --extended-perfdata" );
199 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' ); 201 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' );
200} 202}
diff --git a/plugins/tests/check_curl.t b/plugins/tests/check_curl.t
new file mode 100755
index 0000000..702edb8
--- /dev/null
+++ b/plugins/tests/check_curl.t
@@ -0,0 +1,418 @@
1#! /usr/bin/perl -w -I ..
2#
3# Test check_http by having an actual HTTP server running
4#
5# To create the https server certificate:
6# openssl req -new -x509 -keyout server-key.pem -out server-cert.pem -days 3650 -nodes
7# Country Name (2 letter code) [AU]:UK
8# State or Province Name (full name) [Some-State]:Derbyshire
9# Locality Name (eg, city) []:Belper
10# Organization Name (eg, company) [Internet Widgits Pty Ltd]:Monitoring Plugins
11# Organizational Unit Name (eg, section) []:
12# Common Name (eg, YOUR name) []:Ton Voon
13# Email Address []:tonvoon@mac.com
14
15use strict;
16use Test::More;
17use NPTest;
18use FindBin qw($Bin);
19
20$ENV{'LC_TIME'} = "C";
21
22my $common_tests = 70;
23my $ssl_only_tests = 8;
24# Check that all dependent modules are available
25eval "use HTTP::Daemon 6.01;";
26plan skip_all => 'HTTP::Daemon >= 6.01 required' if $@;
27eval {
28 require HTTP::Status;
29 require HTTP::Response;
30};
31
32my $plugin = 'check_http';
33$plugin = 'check_curl' if $0 =~ m/check_curl/mx;
34
35if ($@) {
36 plan skip_all => "Missing required module for test: $@";
37} else {
38 if (-x "./$plugin") {
39 plan tests => $common_tests * 2 + $ssl_only_tests;
40 } else {
41 plan skip_all => "No $plugin compiled";
42 }
43}
44
45my $servers = { http => 0 }; # HTTP::Daemon should always be available
46eval { require HTTP::Daemon::SSL };
47if ($@) {
48 diag "Cannot load HTTP::Daemon::SSL: $@";
49} else {
50 $servers->{https} = 0;
51}
52
53# set a fixed version, so the header size doesn't vary
54$HTTP::Daemon::VERSION = "1.00";
55
56my $port_http = 50000 + int(rand(1000));
57my $port_https = $port_http + 1;
58my $port_https_expired = $port_http + 2;
59
60# This array keeps sockets around for implementing timeouts
61my @persist;
62
63# Start up all servers
64my @pids;
65my $pid = fork();
66if ($pid) {
67 # Parent
68 push @pids, $pid;
69 if (exists $servers->{https}) {
70 # Fork a normal HTTPS server
71 $pid = fork();
72 if ($pid) {
73 # Parent
74 push @pids, $pid;
75 # Fork an expired cert server
76 $pid = fork();
77 if ($pid) {
78 push @pids, $pid;
79 } else {
80 my $d = HTTP::Daemon::SSL->new(
81 LocalPort => $port_https_expired,
82 LocalAddr => "127.0.0.1",
83 SSL_cert_file => "$Bin/certs/expired-cert.pem",
84 SSL_key_file => "$Bin/certs/expired-key.pem",
85 ) || die;
86 print "Please contact https expired at: <URL:", $d->url, ">\n";
87 run_server( $d );
88 exit;
89 }
90 } else {
91 my $d = HTTP::Daemon::SSL->new(
92 LocalPort => $port_https,
93 LocalAddr => "127.0.0.1",
94 SSL_cert_file => "$Bin/certs/server-cert.pem",
95 SSL_key_file => "$Bin/certs/server-key.pem",
96 ) || die;
97 print "Please contact https at: <URL:", $d->url, ">\n";
98 run_server( $d );
99 exit;
100 }
101 }
102 # give our webservers some time to startup
103 sleep(1);
104} else {
105 # Child
106 #print "child\n";
107 my $d = HTTP::Daemon->new(
108 LocalPort => $port_http,
109 LocalAddr => "127.0.0.1",
110 ) || die;
111 print "Please contact http at: <URL:", $d->url, ">\n";
112 run_server( $d );
113 exit;
114}
115
116# Run the same server on http and https
117sub run_server {
118 my $d = shift;
119 MAINLOOP: while (my $c = $d->accept ) {
120 while (my $r = $c->get_request) {
121 if ($r->method eq "GET" and $r->url->path =~ m^/statuscode/(\d+)^) {
122 $c->send_basic_header($1);
123 $c->send_crlf;
124 } elsif ($r->method eq "GET" and $r->url->path =~ m^/file/(.*)^) {
125 $c->send_basic_header;
126 $c->send_crlf;
127 $c->send_file_response("$Bin/var/$1");
128 } elsif ($r->method eq "GET" and $r->url->path eq "/slow") {
129 $c->send_basic_header;
130 $c->send_crlf;
131 sleep 1;
132 $c->send_response("slow");
133 } elsif ($r->url->path eq "/method") {
134 if ($r->method eq "DELETE") {
135 $c->send_error(HTTP::Status->RC_METHOD_NOT_ALLOWED);
136 } elsif ($r->method eq "foo") {
137 $c->send_error(HTTP::Status->RC_NOT_IMPLEMENTED);
138 } else {
139 $c->send_status_line(200, $r->method);
140 }
141 } elsif ($r->url->path eq "/postdata") {
142 $c->send_basic_header;
143 $c->send_crlf;
144 $c->send_response($r->method.":".$r->content);
145 } elsif ($r->url->path eq "/redirect") {
146 $c->send_redirect( "/redirect2" );
147 } elsif ($r->url->path eq "/redir_external") {
148 $c->send_redirect(($d->isa('HTTP::Daemon::SSL') ? "https" : "http") . "://169.254.169.254/redirect2" );
149 } elsif ($r->url->path eq "/redirect2") {
150 $c->send_basic_header;
151 $c->send_crlf;
152 $c->send_response(HTTP::Response->new( 200, 'OK', undef, 'redirected' ));
153 } elsif ($r->url->path eq "/redir_timeout") {
154 $c->send_redirect( "/timeout" );
155 } elsif ($r->url->path eq "/timeout") {
156 # Keep $c from being destroyed, but prevent severe leaks
157 unshift @persist, $c;
158 delete($persist[1000]);
159 next MAINLOOP;
160 } elsif ($r->url->path eq "/header_check") {
161 $c->send_basic_header;
162 $c->send_header('foo');
163 $c->send_crlf;
164 } else {
165 $c->send_error(HTTP::Status->RC_FORBIDDEN);
166 }
167 $c->close;
168 }
169 }
170}
171
172END {
173 foreach my $pid (@pids) {
174 if ($pid) { print "Killing $pid\n"; kill "INT", $pid }
175 }
176};
177
178if ($ARGV[0] && $ARGV[0] eq "-d") {
179 while (1) {
180 sleep 100;
181 }
182}
183
184my $result;
185my $command = "./$plugin -H 127.0.0.1";
186
187run_common_tests( { command => "$command -p $port_http" } );
188SKIP: {
189 skip "HTTP::Daemon::SSL not installed", $common_tests + $ssl_only_tests if ! exists $servers->{https};
190 run_common_tests( { command => "$command -p $port_https", ssl => 1 } );
191
192 $result = NPTest->testCmd( "$command -p $port_https -S -C 14" );
193 is( $result->return_code, 0, "$command -p $port_https -S -C 14" );
194 is( $result->output, 'OK - Certificate \'Ton Voon\' will expire on Sun Mar 3 21:41:28 2019 +0000.', "output ok" );
195
196 $result = NPTest->testCmd( "$command -p $port_https -S -C 14000" );
197 is( $result->return_code, 1, "$command -p $port_https -S -C 14000" );
198 like( $result->output, '/WARNING - Certificate \'Ton Voon\' expires in \d+ day\(s\) \(Sun Mar 3 21:41:28 2019 \+0000\)./', "output ok" );
199
200 # Expired cert tests
201 $result = NPTest->testCmd( "$command -p $port_https -S -C 13960,14000" );
202 is( $result->return_code, 2, "$command -p $port_https -S -C 13960,14000" );
203 like( $result->output, '/CRITICAL - Certificate \'Ton Voon\' expires in \d+ day\(s\) \(Sun Mar 3 21:41:28 2019 \+0000\)./', "output ok" );
204
205 $result = NPTest->testCmd( "$command -p $port_https_expired -S -C 7" );
206 is( $result->return_code, 2, "$command -p $port_https_expired -S -C 7" );
207 is( $result->output,
208 'CRITICAL - Certificate \'Ton Voon\' expired on Thu Mar 5 00:13:16 2009 +0000.',
209 "output ok" );
210
211}
212
213sub run_common_tests {
214 my ($opts) = @_;
215 my $command = $opts->{command};
216 if ($opts->{ssl}) {
217 $command .= " --ssl";
218 }
219
220 $result = NPTest->testCmd( "$command -u /file/root" );
221 is( $result->return_code, 0, "/file/root");
222 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second/', "Output correct" );
223
224 $result = NPTest->testCmd( "$command -u /file/root -s Root" );
225 is( $result->return_code, 0, "/file/root search for string");
226 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second/', "Output correct" );
227
228 $result = NPTest->testCmd( "$command -u /file/root -s NonRoot" );
229 is( $result->return_code, 2, "Missing string check");
230 like( $result->output, qr%^HTTP CRITICAL: HTTP/1\.1 200 OK - string 'NonRoot' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location");
231
232 $result = NPTest->testCmd( "$command -u /file/root -s NonRootWithOver30charsAndMoreFunThanAWetFish" );
233 is( $result->return_code, 2, "Missing string check");
234 like( $result->output, qr%HTTP CRITICAL: HTTP/1\.1 200 OK - string 'NonRootWithOver30charsAndM...' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location");
235
236 $result = NPTest->testCmd( "$command -u /header_check -d foo" );
237 is( $result->return_code, 0, "header_check search for string");
238 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 96 bytes in [\d\.]+ second/', "Output correct" );
239
240 $result = NPTest->testCmd( "$command -u /header_check -d bar" );
241 is( $result->return_code, 2, "Missing header string check");
242 like( $result->output, qr%^HTTP CRITICAL: HTTP/1\.1 200 OK - header 'bar' not found on 'https?://127\.0\.0\.1:\d+/header_check'%, "Shows search string and location");
243
244 my $cmd;
245 $cmd = "$command -u /slow";
246 $result = NPTest->testCmd( $cmd );
247 is( $result->return_code, 0, "$cmd");
248 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
249 $result->output =~ /in ([\d\.]+) second/;
250 cmp_ok( $1, ">", 1, "Time is > 1 second" );
251
252 $cmd = "$command -u /statuscode/200";
253 $result = NPTest->testCmd( $cmd );
254 is( $result->return_code, 0, $cmd);
255 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
256
257 $cmd = "$command -u /statuscode/200 -e 200";
258 $result = NPTest->testCmd( $cmd );
259 is( $result->return_code, 0, $cmd);
260 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - Status line output matched "200" - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
261
262 $cmd = "$command -u /statuscode/201";
263 $result = NPTest->testCmd( $cmd );
264 is( $result->return_code, 0, $cmd);
265 like( $result->output, '/^HTTP OK: HTTP/1.1 201 Created - \d+ bytes in [\d\.]+ second /', "Output correct: ".$result->output );
266
267 $cmd = "$command -u /statuscode/201 -e 201";
268 $result = NPTest->testCmd( $cmd );
269 is( $result->return_code, 0, $cmd);
270 like( $result->output, '/^HTTP OK: HTTP/1.1 201 Created - Status line output matched "201" - \d+ bytes in [\d\.]+ second /', "Output correct: ".$result->output );
271
272 $cmd = "$command -u /statuscode/201 -e 200";
273 $result = NPTest->testCmd( $cmd );
274 is( $result->return_code, 2, $cmd);
275 like( $result->output, '/^HTTP CRITICAL - Invalid HTTP response received from host on port \d+: HTTP/1.1 201 Created/', "Output correct: ".$result->output );
276
277 $cmd = "$command -u /statuscode/200 -e 200,201,202";
278 $result = NPTest->testCmd( $cmd );
279 is( $result->return_code, 0, $cmd);
280 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - Status line output matched "200,201,202" - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
281
282 $cmd = "$command -u /statuscode/201 -e 200,201,202";
283 $result = NPTest->testCmd( $cmd );
284 is( $result->return_code, 0, $cmd);
285 like( $result->output, '/^HTTP OK: HTTP/1.1 201 Created - Status line output matched "200,201,202" - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
286
287 $cmd = "$command -u /statuscode/203 -e 200,201,202";
288 $result = NPTest->testCmd( $cmd );
289 is( $result->return_code, 2, $cmd);
290 like( $result->output, '/^HTTP CRITICAL - Invalid HTTP response received from host on port (\d+): HTTP/1.1 203 Non-Authoritative Information/', "Output correct: ".$result->output );
291
292 $cmd = "$command -j HEAD -u /method";
293 $result = NPTest->testCmd( $cmd );
294 is( $result->return_code, 0, $cmd);
295 like( $result->output, '/^HTTP OK: HTTP/1.1 200 HEAD - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
296
297 $cmd = "$command -j POST -u /method";
298 $result = NPTest->testCmd( $cmd );
299 is( $result->return_code, 0, $cmd);
300 like( $result->output, '/^HTTP OK: HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
301
302 $cmd = "$command -j GET -u /method";
303 $result = NPTest->testCmd( $cmd );
304 is( $result->return_code, 0, $cmd);
305 like( $result->output, '/^HTTP OK: HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
306
307 $cmd = "$command -u /method";
308 $result = NPTest->testCmd( $cmd );
309 is( $result->return_code, 0, $cmd);
310 like( $result->output, '/^HTTP OK: HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
311
312 $cmd = "$command -P foo -u /method";
313 $result = NPTest->testCmd( $cmd );
314 is( $result->return_code, 0, $cmd);
315 like( $result->output, '/^HTTP OK: HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
316
317 $cmd = "$command -j DELETE -u /method";
318 $result = NPTest->testCmd( $cmd );
319 is( $result->return_code, 1, $cmd);
320 like( $result->output, '/^HTTP WARNING: HTTP/1.1 405 Method Not Allowed/', "Output correct: ".$result->output );
321
322 $cmd = "$command -j foo -u /method";
323 $result = NPTest->testCmd( $cmd );
324 is( $result->return_code, 2, $cmd);
325 like( $result->output, '/^HTTP CRITICAL: HTTP/1.1 501 Not Implemented/', "Output correct: ".$result->output );
326
327 $cmd = "$command -P stufftoinclude -u /postdata -s POST:stufftoinclude";
328 $result = NPTest->testCmd( $cmd );
329 is( $result->return_code, 0, $cmd);
330 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
331
332 $cmd = "$command -j PUT -P stufftoinclude -u /postdata -s PUT:stufftoinclude";
333 $result = NPTest->testCmd( $cmd );
334 is( $result->return_code, 0, $cmd);
335 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
336
337 # To confirm that the free doesn't segfault
338 $cmd = "$command -P stufftoinclude -j PUT -u /postdata -s PUT:stufftoinclude";
339 $result = NPTest->testCmd( $cmd );
340 is( $result->return_code, 0, $cmd);
341 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
342
343 $cmd = "$command -u /redirect";
344 $result = NPTest->testCmd( $cmd );
345 is( $result->return_code, 0, $cmd);
346 like( $result->output, '/^HTTP OK: HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
347
348 $cmd = "$command -f follow -u /redirect";
349 $result = NPTest->testCmd( $cmd );
350 is( $result->return_code, 0, $cmd);
351 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
352
353 $cmd = "$command -u /redirect -k 'follow: me'";
354 $result = NPTest->testCmd( $cmd );
355 is( $result->return_code, 0, $cmd);
356 like( $result->output, '/^HTTP OK: HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
357
358 $cmd = "$command -f follow -u /redirect -k 'follow: me'";
359 $result = NPTest->testCmd( $cmd );
360 is( $result->return_code, 0, $cmd);
361 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
362
363 $cmd = "$command -f sticky -u /redirect -k 'follow: me'";
364 $result = NPTest->testCmd( $cmd );
365 is( $result->return_code, 0, $cmd);
366 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
367
368 $cmd = "$command -f stickyport -u /redirect -k 'follow: me'";
369 $result = NPTest->testCmd( $cmd );
370 is( $result->return_code, 0, $cmd);
371 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
372
373 # These tests may block
374 print "ALRM\n";
375
376 # stickyport - on full urlS port is set back to 80 otherwise
377 $cmd = "$command -f stickyport -u /redir_external -t 5 -s redirected";
378 eval {
379 local $SIG{ALRM} = sub { die "alarm\n" };
380 alarm(2);
381 $result = NPTest->testCmd( $cmd );
382 alarm(0); };
383 isnt( $@, "alarm\n", $cmd );
384 is( $result->return_code, 0, $cmd );
385
386 # Let's hope there won't be any web server on :80 returning "redirected"!
387 $cmd = "$command -f sticky -u /redir_external -t 5 -s redirected";
388 eval {
389 local $SIG{ALRM} = sub { die "alarm\n" };
390 alarm(2);
391 $result = NPTest->testCmd( $cmd );
392 alarm(0); };
393 isnt( $@, "alarm\n", $cmd );
394 isnt( $result->return_code, 0, $cmd );
395
396 # Test an external address - timeout
397 SKIP: {
398 skip "This doesn't seems to work all the time", 1 unless ($ENV{HTTP_EXTERNAL});
399 $cmd = "$command -f follow -u /redir_external -t 5";
400 eval {
401 $result = NPTest->testCmd( $cmd, 2 );
402 };
403 like( $@, "/timeout in command: $cmd/", $cmd );
404 }
405
406 $cmd = "$command -u /timeout -t 5";
407 eval {
408 $result = NPTest->testCmd( $cmd, 2 );
409 };
410 like( $@, "/timeout in command: $cmd/", $cmd );
411
412 $cmd = "$command -f follow -u /redir_timeout -t 2";
413 eval {
414 $result = NPTest->testCmd( $cmd, 5 );
415 };
416 is( $@, "", $cmd );
417
418}
diff --git a/plugins/tests/check_http.t b/plugins/tests/check_http.t
index d6d31de..c9e6595 100755
--- a/plugins/tests/check_http.t
+++ b/plugins/tests/check_http.t
@@ -30,13 +30,16 @@ eval {
30 require HTTP::Response; 30 require HTTP::Response;
31}; 31};
32 32
33my $plugin = 'check_http';
34$plugin = 'check_curl' if $0 =~ m/check_curl/mx;
35
33if ($@) { 36if ($@) {
34 plan skip_all => "Missing required module for test: $@"; 37 plan skip_all => "Missing required module for test: $@";
35} else { 38} else {
36 if (-x "./check_http") { 39 if (-x "./check_http") {
37 plan tests => $common_tests * 2 + $ssl_only_tests + $virtual_port_tests; 40 plan tests => $common_tests * 2 + $ssl_only_tests + $virtual_port_tests;
38 } else { 41 } else {
39 plan skip_all => "No check_http compiled"; 42 plan skip_all => "No $plugin compiled";
40 } 43 }
41} 44}
42 45
@@ -185,7 +188,7 @@ if ($ARGV[0] && $ARGV[0] eq "-d") {
185} 188}
186 189
187my $result; 190my $result;
188my $command = "./check_http -H 127.0.0.1"; 191my $command = "./$plugin -H 127.0.0.1";
189 192
190run_common_tests( { command => "$command -p $port_http" } ); 193run_common_tests( { command => "$command -p $port_http" } );
191SKIP: { 194SKIP: {
diff --git a/po/de.po b/po/de.po
index 72694e2..919fae3 100644
--- a/po/de.po
+++ b/po/de.po
@@ -13,10 +13,10 @@ msgstr ""
13"PO-Revision-Date: 2004-12-23 17:46+0100\n" 13"PO-Revision-Date: 2004-12-23 17:46+0100\n"
14"Last-Translator: <>\n" 14"Last-Translator: <>\n"
15"Language-Team: English <en@li.org>\n" 15"Language-Team: English <en@li.org>\n"
16"Language: en\n"
17"MIME-Version: 1.0\n" 16"MIME-Version: 1.0\n"
18"Content-Type: text/plain; charset=iso-8859-1\n" 17"Content-Type: text/plain; charset=iso-8859-1\n"
19"Content-Transfer-Encoding: 8bit\n" 18"Content-Transfer-Encoding: 8bit\n"
19"Language: en\n"
20"Plural-Forms: nplurals=2; plural=(n > 1);X-Generator: KBabel 1.3.1\n" 20"Plural-Forms: nplurals=2; plural=(n > 1);X-Generator: KBabel 1.3.1\n"
21 21
22#: plugins/check_by_ssh.c:86 plugins/check_cluster.c:76 plugins/check_dig.c:88 22#: plugins/check_by_ssh.c:86 plugins/check_cluster.c:76 plugins/check_dig.c:88
@@ -5438,8 +5438,8 @@ msgstr ""
5438 5438
5439#: plugins/negate.c:174 5439#: plugins/negate.c:174
5440msgid "" 5440msgid ""
5441"Ok must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer " 5441"Ok must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-"
5442"(0-3)." 5442"3)."
5443msgstr "" 5443msgstr ""
5444 5444
5445#: plugins/negate.c:180 5445#: plugins/negate.c:180