summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore7
-rw-r--r--.travis.yml2
-rw-r--r--ACKNOWLEDGEMENTS8
-rw-r--r--REQUIREMENTS16
-rw-r--r--configure.ac39
-rw-r--r--m4/libcurl.m4272
-rw-r--r--m4/uriparser.m4140
-rw-r--r--plugins/Makefile.am9
-rw-r--r--plugins/check_curl.c2324
-rw-r--r--plugins/picohttpparser/Makefile.am3
-rw-r--r--plugins/picohttpparser/picohttpparser.c645
-rw-r--r--plugins/picohttpparser/picohttpparser.h87
-rw-r--r--plugins/sslutils.c33
-rw-r--r--plugins/t/check_curl.t216
-rw-r--r--plugins/t/check_http.t76
-rwxr-xr-xplugins/tests/check_curl.t497
-rwxr-xr-xplugins/tests/check_http.t9
-rw-r--r--po/de.po6
18 files changed, 4330 insertions, 59 deletions
diff --git a/.gitignore b/.gitignore
index 0c16add..c7b668e 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
@@ -202,6 +203,12 @@ NP-VERSION-FILE
202/plugins/stamp-h* 203/plugins/stamp-h*
203/plugins/urlize 204/plugins/urlize
204 205
206# /plugins/picohttpparser
207/plugins/picohttpparser/Makefile
208/plugins/picohttpparser/Makefile.in
209/plugins/picohttpparser/.deps
210/plugins/picohttpparser/libpicohttpparser.a
211
205# /plugins/t/ 212# /plugins/t/
206/plugins/t/*.tmp 213/plugins/t/*.tmp
207 214
diff --git a/.travis.yml b/.travis.yml
index 946345c..3d9fe64 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -51,6 +51,8 @@ install:
51 - sudo apt-get install -qq --no-install-recommends autoconf automake 51 - sudo apt-get install -qq --no-install-recommends autoconf automake
52 - sudo apt-get install -qq --no-install-recommends faketime 52 - sudo apt-get install -qq --no-install-recommends faketime
53 - sudo apt-get install -qq --no-install-recommends libmonitoring-plugin-perl 53 - sudo apt-get install -qq --no-install-recommends libmonitoring-plugin-perl
54 - sudo apt-get install -qq --no-install-recommends libcurl4-openssl-dev
55 - sudo apt-get install -qq --no-install-recommends liburiparser-dev
54 - sudo apt-get install -qq --no-install-recommends squid 56 - sudo apt-get install -qq --no-install-recommends squid
55 # Trusty related dependencies (not yet provided) 57 # Trusty related dependencies (not yet provided)
56 - test "$(dpkg -l | grep -E "mysql-(client|server)-[0-9].[0-9]" | grep -c ^ii)" -gt 0 || sudo apt-get install -qq --no-install-recommends mariadb-client mariadb-server 58 - test "$(dpkg -l | grep -E "mysql-(client|server)-[0-9].[0-9]" | grep -c ^ii)" -gt 0 || sudo apt-get install -qq --no-install-recommends mariadb-client mariadb-server
diff --git a/ACKNOWLEDGEMENTS b/ACKNOWLEDGEMENTS
index 50c714c..d73be54 100644
--- a/ACKNOWLEDGEMENTS
+++ b/ACKNOWLEDGEMENTS
@@ -20,7 +20,7 @@ Using the DLPI support on SysV systems to get the host MAC address in check_dhcp
20Stenberg, Daniel 20Stenberg, Daniel
21Copyright (c) 1996 - 2004, Daniel Stenberg, <daniel@haxx.se> 21Copyright (c) 1996 - 2004, Daniel Stenberg, <daniel@haxx.se>
22http://curl.haxx.se/ 22http://curl.haxx.se/
23Use of duplication of macros in m4/np_curl.m4 23Use of duplication of macros in m4/np_curl.m4 (slighly adapted for m4/uriparser.m4 too)
24 24
25Coreutils team 25Coreutils team
26Copyright (C) 91, 1995-2004 Free Software Foundation, Inc. 26Copyright (C) 91, 1995-2004 Free Software Foundation, Inc.
@@ -31,3 +31,9 @@ Gnulib team
31Copyright (C) 2001, 2003, 2004, 2006 Free Software Foundation, Inc 31Copyright (C) 2001, 2003, 2004, 2006 Free Software Foundation, Inc
32http://www.gnu.org/software/gnulib/ 32http://www.gnu.org/software/gnulib/
33Use of lib files that originally were used from coreutils 33Use of lib files that originally were used from coreutils
34
35Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
36 Shigeo Mitsunari
37picohttpparser
38https://github.com/h2o/picohttpparser
39Use of the library for HTTP header parsing in check_curl.
diff --git a/REQUIREMENTS b/REQUIREMENTS
index ac7b593..f3b1c01 100644
--- a/REQUIREMENTS
+++ b/REQUIREMENTS
@@ -11,6 +11,22 @@ check_ldaps, check_http --ssl, check_tcp --ssl, check_smtp --starttls
11 - Requires openssl or gnutls libraries for SSL connections 11 - Requires openssl or gnutls libraries for SSL connections
12 http://www.openssl.org, http://www.gnu.org/software/gnutls 12 http://www.openssl.org, http://www.gnu.org/software/gnutls
13 13
14check_curl:
15 - Requires libcurl 7.15.2 or later
16 http://www.haxx.se
17 - --ssl/-S and -C requires OpenSSL for certificate checks, otherwise
18 libcurl must be quite new to support CURLINFO_CERTINFO with
19 GnuTLS and NSS libraries:
20 - 7.42.0 or newer for GnuTLS
21 - 7.34.0 or newer for NSS
22 GnuTLS is known to create problems on some distributions with
23 self-signed certificate chains
24 http://www.openssl.org, http://www.gnu.org/software/gnutls,
25 http://www.mozilla.org/projects/security/pki/nss/,
26 other SSL implementations are currently not supported
27 - uriparser 0.7.5 or later
28 https://uriparser.github.io/
29
14check_fping: 30check_fping:
15 - Requires the fping utility distributed with SATAN. Either 31 - Requires the fping utility distributed with SATAN. Either
16 download and install SATAN or grab the fping program from 32 download and install SATAN or grab the fping program from
diff --git a/configure.ac b/configure.ac
index 4a9a92a..4aebc2a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -385,6 +385,42 @@ if test "$ac_cv_header_wtsapi32_h" = "yes"; then
385 AC_SUBST(WTSAPI32LIBS) 385 AC_SUBST(WTSAPI32LIBS)
386fi 386fi
387 387
388_can_enable_check_curl=no
389dnl Check for cURL library
390LIBCURL_CHECK_CONFIG(yes, 7.15.2, [
391 _can_enable_check_curl=yes
392 LIBCURLINCLUDE="$LIBCURL_CPPFLAGS"
393 LIBCURLLIBS="$LIBCURL"
394 LIBCURLCFLAGS="$LIBCURL_CPPFLAGS"
395 AC_SUBST(LIBCURLINCLUDE)
396 AC_SUBST(LIBCURLLIBS)
397 AC_SUBST(LIBCURLCFLAGS)
398 ], [
399 _can_enable_check_curl=no
400 AC_MSG_WARN([Skipping curl plugin])
401 AC_MSG_WARN([install libcurl libs to compile this plugin (see REQUIREMENTS).])
402])
403
404dnl Check for uriparser library
405URIPARSER_CHECK(yes, 0.7.5, [
406 URIPARSERINCLUDE="$URIPARSER_CPPFLAGS"
407 URIPARSERLIBS="$URIPARSER"
408 URIPARSERCFLAGS="$URIPARSER_CPPFLAGS"
409 AC_SUBST(URIPARSERINCLUDE)
410 AC_SUBST(URIPARSERLIBS)
411 AC_SUBST(URIPARSERCFLAGS)
412 ], [
413 _can_enable_check_curl=no
414 AC_MSG_WARN([Skipping curl plugin])
415 AC_MSG_WARN([install the uriparser library to compile this plugin (see REQUIREMENTS).])
416])
417
418dnl prerequisites met, enable the plugin
419if test x$_can_enable_check_curl = xyes; then
420 EXTRAS="$EXTRAS check_curl\$(EXEEXT)"
421fi
422AC_CONFIG_FILES([plugins/picohttpparser/Makefile])
423
388dnl Fallback to who(1) if the system doesn't provide an utmpx(5) interface 424dnl 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" 425if test "$ac_cv_header_utmpx_h" = "no" -a "$ac_cv_header_wtsapi32_h" = "no"
390then 426then
@@ -1894,4 +1930,5 @@ ACX_FEATURE([enable],[perl-modules])
1894ACX_FEATURE([with],[cgiurl]) 1930ACX_FEATURE([with],[cgiurl])
1895ACX_FEATURE([with],[trusted-path]) 1931ACX_FEATURE([with],[trusted-path])
1896ACX_FEATURE([enable],[libtap]) 1932ACX_FEATURE([enable],[libtap])
1897 1933ACX_FEATURE([with],[libcurl])
1934ACX_FEATURE([with],[uriparser])
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/m4/uriparser.m4 b/m4/uriparser.m4
new file mode 100644
index 0000000..dbb8a55
--- /dev/null
+++ b/m4/uriparser.m4
@@ -0,0 +1,140 @@
1# (this check is rougly based on and inspired libcurl.m4)
2# URIPARSER_CHECK ([DEFAULT-ACTION], [MINIMUM-VERSION],
3# [ACTION-IF-YES], [ACTION-IF-NO])
4# Checks for uriparser library. DEFAULT-ACTION is the string yes or no to
5# specify whether to default to --with-uriparser or --without-liburiparser.
6# If not supplied, DEFAULT-ACTION is yes. MINIMUM-VERSION is the
7# minimum version of uriparser to accept. Pass the version as a regular
8# version number like 0.8.5. If not supplied, any version is
9# accepted. ACTION-IF-YES is a list of shell commands to run if
10# uriparser was successfully found and passed the various tests.
11# ACTION-IF-NO is a list of shell commands that are run otherwise.
12# Note that using --without-uriparser does run ACTION-IF-NO.
13#
14# This macro #defines HAVE_URIPARSER if a working uriparser setup is
15# found, and sets @URIPARSER@ and @URIPARSER_CPPFLAGS@ to the necessary
16# values.
17#
18# Users may override the detected values by doing something like:
19# URIPARSER="-luriparser" URIPARSER_CPPFLAGS="-I/usr/myinclude" ./configure
20#
21
22AC_DEFUN([URIPARSER_CHECK],
23[
24 AC_ARG_WITH(uriparser,
25 AS_HELP_STRING([--with-uriparser=PREFIX],[look for the uriparser library in PREFIX/lib and headers in PREFIX/include]),
26 [_uriparser_with=$withval],[_uriparser_with=ifelse([$1],,[yes],[$1])])
27
28 if test "$_uriparser_with" != "no" ; then
29
30 _uriparser_try_link=yes
31
32 AC_CHECK_PROG(PKGCONFIG,pkg-config,pkg-config,no)
33
34 if test "x$URIPARSER" != "x" || test "x$URIPARSER_CPPFLAGS" != "x"; then
35 :
36 elif test -d "$_uriparser_with" ; then
37 URIPARSER_CPPFLAGS="-I$withval/include"
38 _uriparser_ldflags="-L$withval/lib"
39
40 elif test x$PKGCONFIG != xno; then
41
42 AC_CACHE_CHECK([for the version of uriparser],
43 [uriparser_cv_uriparser_version],
44 [uriparser_cv_uriparser_version=`$PKGCONFIG liburiparser --modversion`])
45
46 AC_PROG_AWK
47
48 _uriparser_version_parse="eval $AWK '{split(\$NF,A,\".\"); X=256*256*A[[1]]+256*A[[2]]+A[[3]]; print X;}'"
49
50 _uriparser_version=`echo $uriparser_cv_uriparser_version | $_uriparser_version_parse`
51 _uriparser_wanted=`echo ifelse([$2],,[0],[$2]) | $_uriparser_version_parse`
52
53 if test $_uriparser_wanted -gt 0 ; then
54 AC_CACHE_CHECK([for uriparser >= version $2],
55 [uriparser_cv_lib_version_ok],
56 [
57 if test $_uriparser_version -ge $_uriparser_wanted ; then
58 uriparser_cv_lib_version_ok=yes
59 else
60 uriparser_cv_lib_version_ok=no
61 fi
62 ])
63 fi
64
65 if test $_uriparser_wanted -eq 0 || test x$uriparser_cv_lib_version_ok = xyes ; then
66 if test x"$URIPARSER_CPPFLAGS" = "x" ; then
67 URIPARSER_CPPFLAGS=`$PKGCONFIG liburiparser --cflags`
68 fi
69 if test x"$URIPARSER" = "x" ; then
70 URIPARSER=`$PKGCONFIG liburiparser --libs`
71 fi
72 else
73 _uriparser_try_link=no
74 fi
75
76 unset _uriparser_wanted
77 else
78 dnl no pkg-config, ok, do our best and set some defaults
79 URIPARSER_CPPFLAGS="-I/usr/include"
80 URIPARSER="-luriparser -L/usr/lib -L/usr/lib64 -L/usr/lib/x86_64-linux-gnu -L/usr/lib/i686-linux-gnu"
81 fi
82
83 if test $_uriparser_try_link = yes ; then
84
85 # let's see if the user-supplied
86 # link line (or failing that, "-luriparser") is enough.
87 URIPARSER=${URIPARSER-"$_uriparser_ldflags -luriparser"}
88
89 AC_CACHE_CHECK([whether uriparser is usable],
90 [uriparser_cv_lib_uriparser_usable],
91 [
92 _liburiparser_save_cppflags=$CPPFLAGS
93 CPPFLAGS="$URIPARSER_CPPFLAGS $CPPFLAGS"
94 _liburiparser_save_libs=$LIBS
95 LIBS="$URIPARSER $LIBS"
96
97 AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <uriparser/Uri.h>]],[[
98/* Try and use a few common options to force a failure if we are
99 missing symbols or cannot link. */
100UriParserStateA state;
101UriUriA uri;
102state.uri = &uri;
103char *location = "http://test.dom/dir/file.ext";
104int x = uriParseUriA (&state, location);
105if (x == URI_SUCCESS) {;}
106]])],uriparser_cv_lib_uriparser_usable=yes,uriparser_cv_lib_uriparser_usable=no)
107
108 CPPFLAGS=$_liburiparser_save_cppflags
109 LIBS=$_liburiparser_save_libs
110 unset _liburiparser_save_cppflags
111 unset _liburiparser_save_libs
112 ])
113
114 if test $uriparser_cv_lib_uriparser_usable = yes ; then
115 AC_DEFINE(HAVE_URIPARSER,1,
116 [Define to 1 if you have a functional uriparser library.])
117 AC_SUBST(URIPARSER_CPPFLAGS)
118 AC_SUBST(URIPARSER)
119 else
120 unset URIPARSER
121 unset URIPARSER_CPPFLAGS
122 fi
123 fi
124
125 unset _uriparser_try_link
126 unset _uriparser_version_parse
127 unset _uriparser_version
128 unset _uriparser_ldflags
129 fi
130
131 if test x$_uriparser_with = xno || test x$uriparser_cv_lib_uriparser_usable != xyes ; then
132 # This is the IF-NO path
133 ifelse([$4],,:,[$4])
134 else
135 # This is the IF-YES path
136 ifelse([$3],,:,[$3])
137 fi
138
139 unset _uriparser_with
140])dnl
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 0ddf9bd..3fde54d 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -38,7 +38,9 @@ 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
43SUBDIRS = picohttpparser
42 44
43EXTRA_DIST = t tests 45EXTRA_DIST = t tests
44 46
@@ -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) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser
75check_curl_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser
76check_curl_LDADD = $(NETLIBS) $(LIBCURLLIBS) $(SSLOBJS) $(URIPARSERLIBS) picohttpparser/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..7aae920
--- /dev/null
+++ b/plugins/check_curl.c
@@ -0,0 +1,2324 @@
1/*****************************************************************************
2*
3* Monitoring check_curl plugin
4*
5* License: GPL
6* Copyright (c) 1999-2019 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-2019";
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#include "uriparser/Uri.h"
55
56#include <arpa/inet.h>
57
58#define MAKE_LIBCURL_VERSION(major, minor, patch) ((major)*0x10000 + (minor)*0x100 + (patch))
59
60#define DEFAULT_BUFFER_SIZE 2048
61#define DEFAULT_SERVER_URL "/"
62#define HTTP_EXPECT "HTTP/"
63#define DEFAULT_MAX_REDIRS 15
64#define INET_ADDR_MAX_SIZE INET6_ADDRSTRLEN
65enum {
66 MAX_IPV4_HOSTLENGTH = 255,
67 HTTP_PORT = 80,
68 HTTPS_PORT = 443,
69 MAX_PORT = 65535
70};
71
72enum {
73 STICKY_NONE = 0,
74 STICKY_HOST = 1,
75 STICKY_PORT = 2
76};
77
78enum {
79 FOLLOW_HTTP_CURL = 0,
80 FOLLOW_LIBCURL = 1
81};
82
83/* for buffers for header and body */
84typedef struct {
85 char *buf;
86 size_t buflen;
87 size_t bufsize;
88} curlhelp_write_curlbuf;
89
90/* for buffering the data sent in PUT */
91typedef struct {
92 char *buf;
93 size_t buflen;
94 off_t pos;
95} curlhelp_read_curlbuf;
96
97/* for parsing the HTTP status line */
98typedef struct {
99 int http_major; /* major version of the protocol, always 1 (HTTP/0.9
100 * never reached the big internet most likely) */
101 int http_minor; /* minor version of the protocol, usually 0 or 1 */
102 int http_code; /* HTTP return code as in RFC 2145 */
103 int http_subcode; /* Microsoft IIS extension, HTTP subcodes, see
104 * http://support.microsoft.com/kb/318380/en-us */
105 const char *msg; /* the human readable message */
106 char *first_line; /* a copy of the first line */
107} curlhelp_statusline;
108
109/* to know the underlying SSL library used by libcurl */
110typedef enum curlhelp_ssl_library {
111 CURLHELP_SSL_LIBRARY_UNKNOWN,
112 CURLHELP_SSL_LIBRARY_OPENSSL,
113 CURLHELP_SSL_LIBRARY_LIBRESSL,
114 CURLHELP_SSL_LIBRARY_GNUTLS,
115 CURLHELP_SSL_LIBRARY_NSS
116} curlhelp_ssl_library;
117
118enum {
119 REGS = 2,
120 MAX_RE_SIZE = 256
121};
122#include "regex.h"
123regex_t preg;
124regmatch_t pmatch[REGS];
125char regexp[MAX_RE_SIZE];
126int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
127int errcode;
128int invert_regex = 0;
129
130char *server_address;
131char *host_name;
132char *server_url = 0;
133char server_ip[DEFAULT_BUFFER_SIZE];
134struct curl_slist *server_ips = NULL;
135int specify_port = FALSE;
136unsigned short server_port = HTTP_PORT;
137unsigned short virtual_port = 0;
138int host_name_length;
139char output_header_search[30] = "";
140char output_string_search[30] = "";
141char *warning_thresholds = NULL;
142char *critical_thresholds = NULL;
143int days_till_exp_warn, days_till_exp_crit;
144thresholds *thlds;
145char user_agent[DEFAULT_BUFFER_SIZE];
146int verbose = 0;
147int show_extended_perfdata = FALSE;
148int min_page_len = 0;
149int max_page_len = 0;
150int redir_depth = 0;
151int max_depth = DEFAULT_MAX_REDIRS;
152char *http_method = NULL;
153char *http_post_data = NULL;
154char *http_content_type = NULL;
155CURL *curl;
156struct curl_slist *header_list = NULL;
157curlhelp_write_curlbuf body_buf;
158curlhelp_write_curlbuf header_buf;
159curlhelp_statusline status_line;
160curlhelp_read_curlbuf put_buf;
161char http_header[DEFAULT_BUFFER_SIZE];
162long code;
163long socket_timeout = DEFAULT_SOCKET_TIMEOUT;
164double total_time;
165double time_connect;
166double time_appconnect;
167double time_headers;
168double time_firstbyte;
169char errbuf[CURL_ERROR_SIZE+1];
170CURLcode res;
171char url[DEFAULT_BUFFER_SIZE];
172char msg[DEFAULT_BUFFER_SIZE];
173char perfstring[DEFAULT_BUFFER_SIZE];
174char header_expect[MAX_INPUT_BUFFER] = "";
175char string_expect[MAX_INPUT_BUFFER] = "";
176char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
177int server_expect_yn = 0;
178char user_auth[MAX_INPUT_BUFFER] = "";
179char proxy_auth[MAX_INPUT_BUFFER] = "";
180char **http_opt_headers;
181int http_opt_headers_count = 0;
182int display_html = FALSE;
183int onredirect = STATE_OK;
184int followmethod = FOLLOW_HTTP_CURL;
185int followsticky = STICKY_NONE;
186int use_ssl = FALSE;
187int use_sni = TRUE;
188int check_cert = FALSE;
189typedef union {
190 struct curl_slist* to_info;
191 struct curl_certinfo* to_certinfo;
192} cert_ptr_union;
193cert_ptr_union cert_ptr;
194int ssl_version = CURL_SSLVERSION_DEFAULT;
195char *client_cert = NULL;
196char *client_privkey = NULL;
197char *ca_cert = NULL;
198int is_openssl_callback = FALSE;
199#if defined(HAVE_SSL) && defined(USE_OPENSSL)
200X509 *cert = NULL;
201#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */
202int no_body = FALSE;
203int maximum_age = -1;
204int address_family = AF_UNSPEC;
205curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN;
206int curl_http_version = CURL_HTTP_VERSION_NONE;
207
208int process_arguments (int, char**);
209void handle_curl_option_return_code (CURLcode res, const char* option);
210int check_http (void);
211void redir (curlhelp_write_curlbuf*);
212char *perfd_time (double microsec);
213char *perfd_time_connect (double microsec);
214char *perfd_time_ssl (double microsec);
215char *perfd_time_firstbyte (double microsec);
216char *perfd_time_headers (double microsec);
217char *perfd_time_transfer (double microsec);
218char *perfd_size (int page_len);
219void print_help (void);
220void print_usage (void);
221void print_curl_version (void);
222int curlhelp_initwritebuffer (curlhelp_write_curlbuf*);
223int curlhelp_buffer_write_callback (void*, size_t , size_t , void*);
224void curlhelp_freewritebuffer (curlhelp_write_curlbuf*);
225int curlhelp_initreadbuffer (curlhelp_read_curlbuf *, const char *, size_t);
226int curlhelp_buffer_read_callback (void *, size_t , size_t , void *);
227void curlhelp_freereadbuffer (curlhelp_read_curlbuf *);
228curlhelp_ssl_library curlhelp_get_ssl_library (CURL*);
229const char* curlhelp_get_ssl_library_string (curlhelp_ssl_library);
230int net_noopenssl_check_certificate (cert_ptr_union*, int, int);
231
232int curlhelp_parse_statusline (const char*, curlhelp_statusline *);
233void curlhelp_free_statusline (curlhelp_statusline *);
234char *get_header_value (const struct phr_header* headers, const size_t nof_headers, const char* header);
235int check_document_dates (const curlhelp_write_curlbuf *, char (*msg)[DEFAULT_BUFFER_SIZE]);
236int get_content_length (const curlhelp_write_curlbuf* header_buf, const curlhelp_write_curlbuf* body_buf);
237
238#if defined(HAVE_SSL) && defined(USE_OPENSSL)
239int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit);
240#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */
241
242void remove_newlines (char *);
243void test_file (char *);
244
245int
246main (int argc, char **argv)
247{
248 int result = STATE_UNKNOWN;
249
250 setlocale (LC_ALL, "");
251 bindtextdomain (PACKAGE, LOCALEDIR);
252 textdomain (PACKAGE);
253
254 /* Parse extra opts if any */
255 argv = np_extra_opts (&argc, argv, progname);
256
257 /* set defaults */
258 snprintf( user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)",
259 progname, NP_VERSION, VERSION, curl_version());
260
261 /* parse arguments */
262 if (process_arguments (argc, argv) == ERROR)
263 usage4 (_("Could not parse arguments"));
264
265 if (display_html == TRUE)
266 printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">",
267 use_ssl ? "https" : "http",
268 host_name ? host_name : server_address,
269 virtual_port ? virtual_port : server_port,
270 server_url);
271
272 result = check_http ();
273 return result;
274}
275
276#ifdef HAVE_SSL
277#ifdef USE_OPENSSL
278
279int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
280{
281 /* TODO: we get all certificates of the chain, so which ones
282 * should we test?
283 * TODO: is the last certificate always the server certificate?
284 */
285 cert = X509_STORE_CTX_get_current_cert(x509_ctx);
286 return 1;
287}
288
289CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm)
290{
291 SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER, verify_callback);
292
293 return CURLE_OK;
294}
295
296#endif /* USE_OPENSSL */
297#endif /* HAVE_SSL */
298
299/* Checks if the server 'reply' is one of the expected 'statuscodes' */
300static int
301expected_statuscode (const char *reply, const char *statuscodes)
302{
303 char *expected, *code;
304 int result = 0;
305
306 if ((expected = strdup (statuscodes)) == NULL)
307 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
308
309 for (code = strtok (expected, ","); code != NULL; code = strtok (NULL, ","))
310 if (strstr (reply, code) != NULL) {
311 result = 1;
312 break;
313 }
314
315 free (expected);
316 return result;
317}
318
319void
320handle_curl_option_return_code (CURLcode res, const char* option)
321{
322 if (res != CURLE_OK) {
323 snprintf (msg, DEFAULT_BUFFER_SIZE, _("Error while setting cURL option '%s': cURL returned %d - %s"),
324 option, res, curl_easy_strerror(res));
325 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
326 }
327}
328
329int
330check_http (void)
331{
332 int result = STATE_OK;
333 int page_len = 0;
334 int i;
335 char *force_host_header = NULL;
336
337 /* initialize curl */
338 if (curl_global_init (CURL_GLOBAL_DEFAULT) != CURLE_OK)
339 die (STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n");
340
341 if ((curl = curl_easy_init()) == NULL)
342 die (STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n");
343
344 if (verbose >= 1)
345 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_VERBOSE, TRUE), "CURLOPT_VERBOSE");
346
347 /* print everything on stdout like check_http would do */
348 handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_STDERR, stdout), "CURLOPT_STDERR");
349
350 /* initialize buffer for body of the answer */
351 if (curlhelp_initwritebuffer(&body_buf) < 0)
352 die (STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n");
353 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback), "CURLOPT_WRITEFUNCTION");
354 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_WRITEDATA, (void *)&body_buf), "CURLOPT_WRITEDATA");
355
356 /* initialize buffer for header of the answer */
357 if (curlhelp_initwritebuffer( &header_buf ) < 0)
358 die (STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n" );
359 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback), "CURLOPT_HEADERFUNCTION");
360 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_WRITEHEADER, (void *)&header_buf), "CURLOPT_WRITEHEADER");
361
362 /* set the error buffer */
363 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, errbuf), "CURLOPT_ERRORBUFFER");
364
365 /* set timeouts */
366 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, socket_timeout), "CURLOPT_CONNECTTIMEOUT");
367 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_TIMEOUT, socket_timeout), "CURLOPT_TIMEOUT");
368
369 /* compose URL: use the address we want to connect to, set Host: header later */
370 snprintf (url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s",
371 use_ssl ? "https" : "http",
372 use_ssl ? host_name : server_address,
373 server_port,
374 server_url
375 );
376
377 if (verbose>=1)
378 printf ("* curl CURLOPT_URL: %s\n", url);
379 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_URL, url), "CURLOPT_URL");
380
381 /* extract proxy information for legacy proxy https requests */
382 if (!strcmp(http_method, "CONNECT") || strstr(server_url, "http") == server_url) {
383 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_PROXY, server_address), "CURLOPT_PROXY");
384 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_PROXYPORT, (long)server_port), "CURLOPT_PROXYPORT");
385 if (verbose>=2)
386 printf ("* curl CURLOPT_PROXY: %s:%d\n", server_address, server_port);
387 http_method = "GET";
388 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_URL, server_url), "CURLOPT_URL");
389 }
390
391 /* disable body for HEAD request */
392 if (http_method && !strcmp (http_method, "HEAD" )) {
393 no_body = TRUE;
394 }
395
396 /* set HTTP protocol version */
397 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_HTTP_VERSION, curl_http_version), "CURLOPT_HTTP_VERSION");
398
399 /* set HTTP method */
400 if (http_method) {
401 if (!strcmp(http_method, "POST"))
402 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_POST, 1), "CURLOPT_POST");
403 else if (!strcmp(http_method, "PUT"))
404 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD");
405 else
406 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CUSTOMREQUEST, http_method), "CURLOPT_CUSTOMREQUEST");
407 }
408
409 /* check if Host header is explicitly set in options */
410 if (http_opt_headers_count) {
411 for (i = 0; i < http_opt_headers_count ; i++) {
412 if (strncmp(http_opt_headers[i], "Host:", 5) == 0) {
413 force_host_header = http_opt_headers[i];
414 }
415 }
416 }
417
418 /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in anyway */
419 if(host_name != NULL && force_host_header == NULL) {
420 if((virtual_port != HTTP_PORT && !use_ssl) || (virtual_port != HTTPS_PORT && use_ssl)) {
421 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", host_name, virtual_port);
422 } else {
423 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", host_name);
424 }
425 header_list = curl_slist_append (header_list, http_header);
426 }
427
428 /* always close connection, be nice to servers */
429 snprintf (http_header, DEFAULT_BUFFER_SIZE, "Connection: close");
430 header_list = curl_slist_append (header_list, http_header);
431
432 /* attach additional headers supplied by the user */
433 /* optionally send any other header tag */
434 if (http_opt_headers_count) {
435 for (i = 0; i < http_opt_headers_count ; i++) {
436 header_list = curl_slist_append (header_list, http_opt_headers[i]);
437 }
438 /* This cannot be free'd here because a redirection will then try to access this and segfault */
439 /* Covered in a testcase in tests/check_http.t */
440 /* free(http_opt_headers); */
441 }
442
443 /* set HTTP headers */
444 handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPHEADER, header_list ), "CURLOPT_HTTPHEADER");
445
446#ifdef LIBCURL_FEATURE_SSL
447
448 /* set SSL version, warn about unsecure or unsupported versions */
449 if (use_ssl) {
450 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSLVERSION, ssl_version), "CURLOPT_SSLVERSION");
451 }
452
453 /* client certificate and key to present to server (SSL) */
454 if (client_cert)
455 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSLCERT, client_cert), "CURLOPT_SSLCERT");
456 if (client_privkey)
457 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSLKEY, client_privkey), "CURLOPT_SSLKEY");
458 if (ca_cert) {
459 /* per default if we have a CA verify both the peer and the
460 * hostname in the certificate, can be switched off later */
461 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CAINFO, ca_cert), "CURLOPT_CAINFO");
462 handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_SSL_VERIFYPEER, 1), "CURLOPT_SSL_VERIFYPEER");
463 handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_SSL_VERIFYHOST, 2), "CURLOPT_SSL_VERIFYHOST");
464 } else {
465 /* backward-compatible behaviour, be tolerant in checks
466 * TODO: depending on more options have aspects we want
467 * to be less tolerant about ssl verfications
468 */
469 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0), "CURLOPT_SSL_VERIFYPEER");
470 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0), "CURLOPT_SSL_VERIFYHOST");
471 }
472
473 /* detect SSL library used by libcurl */
474 ssl_library = curlhelp_get_ssl_library (curl);
475
476 /* try hard to get a stack of certificates to verify against */
477 if (check_cert) {
478#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1)
479 /* inform curl to report back certificates */
480 switch (ssl_library) {
481 case CURLHELP_SSL_LIBRARY_OPENSSL:
482 case CURLHELP_SSL_LIBRARY_LIBRESSL:
483 /* set callback to extract certificate with OpenSSL context function (works with
484 * OpenSSL-style libraries only!) */
485#ifdef USE_OPENSSL
486 /* libcurl and monitoring plugins built with OpenSSL, good */
487 handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION");
488 is_openssl_callback = TRUE;
489#else /* USE_OPENSSL */
490#endif /* USE_OPENSSL */
491 /* libcurl is built with OpenSSL, monitoring plugins, so falling
492 * back to manually extracting certificate information */
493 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
494 break;
495
496 case CURLHELP_SSL_LIBRARY_NSS:
497#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
498 /* NSS: support for CERTINFO is implemented since 7.34.0 */
499 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
500#else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
501 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));
502#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
503 break;
504
505 case CURLHELP_SSL_LIBRARY_GNUTLS:
506#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0)
507 /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */
508 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
509#else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
510 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));
511#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
512 break;
513
514 case CURLHELP_SSL_LIBRARY_UNKNOWN:
515 default:
516 die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (unknown SSL library '%s', must implement first)\n", curlhelp_get_ssl_library_string (ssl_library));
517 break;
518 }
519#else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
520 /* old libcurl, our only hope is OpenSSL, otherwise we are out of luck */
521 if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL || ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL)
522 handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION");
523 else
524 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");
525#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
526 }
527
528#endif /* LIBCURL_FEATURE_SSL */
529
530 /* set default or user-given user agent identification */
531 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_USERAGENT, user_agent), "CURLOPT_USERAGENT");
532
533 /* proxy-authentication */
534 if (strcmp(proxy_auth, ""))
535 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_PROXYUSERPWD, proxy_auth), "CURLOPT_PROXYUSERPWD");
536
537 /* authentication */
538 if (strcmp(user_auth, ""))
539 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_USERPWD, user_auth), "CURLOPT_USERPWD");
540
541 /* TODO: parameter auth method, bitfield of following methods:
542 * CURLAUTH_BASIC (default)
543 * CURLAUTH_DIGEST
544 * CURLAUTH_DIGEST_IE
545 * CURLAUTH_NEGOTIATE
546 * CURLAUTH_NTLM
547 * CURLAUTH_NTLM_WB
548 *
549 * convenience tokens for typical sets of methods:
550 * CURLAUTH_ANYSAFE: most secure, without BASIC
551 * or CURLAUTH_ANY: most secure, even BASIC if necessary
552 *
553 * handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_DIGEST ), "CURLOPT_HTTPAUTH");
554 */
555
556 /* handle redirections */
557 if (onredirect == STATE_DEPENDENT) {
558 if( followmethod == FOLLOW_LIBCURL ) {
559 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1), "CURLOPT_FOLLOWLOCATION");
560
561 /* default -1 is infinite, not good, could lead to zombie plugins!
562 Setting it to one bigger than maximal limit to handle errors nicely below
563 */
564 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_MAXREDIRS, max_depth+1), "CURLOPT_MAXREDIRS");
565
566 /* for now allow only http and https (we are a http(s) check plugin in the end) */
567#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4)
568 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS), "CURLOPT_REDIRECT_PROTOCOLS");
569#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4) */
570
571 /* TODO: handle the following aspects of redirection, make them
572 * command line options too later:
573 CURLOPT_POSTREDIR: method switch
574 CURLINFO_REDIRECT_URL: custom redirect option
575 CURLOPT_REDIRECT_PROTOCOLS: allow people to step outside safe protocols
576 CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range option here is nice like for expected page size?
577 */
578 } else {
579 /* old style redirection is handled below */
580 }
581 }
582
583 /* no-body */
584 if (no_body)
585 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_NOBODY, 1), "CURLOPT_NOBODY");
586
587 /* IPv4 or IPv6 forced DNS resolution */
588 if (address_family == AF_UNSPEC)
589 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)");
590 else if (address_family == AF_INET)
591 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)");
592#if defined (USE_IPV6) && defined (LIBCURL_FEATURE_IPV6)
593 else if (address_family == AF_INET6)
594 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)");
595#endif
596
597 /* either send http POST data (any data, not only POST)*/
598 if (!strcmp(http_method, "POST") ||!strcmp(http_method, "PUT")) {
599 /* set content of payload for POST and PUT */
600 if (http_content_type) {
601 snprintf (http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", http_content_type);
602 header_list = curl_slist_append (header_list, http_header);
603 }
604 /* NULL indicates "HTTP Continue" in libcurl, provide an empty string
605 * in case of no POST/PUT data */
606 if (!http_post_data)
607 http_post_data = "";
608 if (!strcmp(http_method, "POST")) {
609 /* POST method, set payload with CURLOPT_POSTFIELDS */
610 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_POSTFIELDS, http_post_data), "CURLOPT_POSTFIELDS");
611 } else if (!strcmp(http_method, "PUT")) {
612 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_READFUNCTION, (curl_read_callback)curlhelp_buffer_read_callback), "CURLOPT_READFUNCTION");
613 curlhelp_initreadbuffer (&put_buf, http_post_data, strlen (http_post_data));
614 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_READDATA, (void *)&put_buf), "CURLOPT_READDATA");
615 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_INFILESIZE, (curl_off_t)strlen (http_post_data)), "CURLOPT_INFILESIZE");
616 }
617 }
618
619 /* do the request */
620 res = curl_easy_perform(curl);
621
622 if (verbose>=2 && http_post_data)
623 printf ("**** REQUEST CONTENT ****\n%s\n", http_post_data);
624
625 /* free header and server IP resolve lists, we don't need it anymore */
626 curl_slist_free_all (header_list); header_list = NULL;
627 curl_slist_free_all (server_ips); server_ips = NULL;
628
629 /* Curl errors, result in critical Nagios state */
630 if (res != CURLE_OK) {
631 snprintf (msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"),
632 server_port, res, curl_easy_strerror(res));
633 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
634 }
635
636 /* certificate checks */
637#ifdef LIBCURL_FEATURE_SSL
638 if (use_ssl == TRUE) {
639 if (check_cert == TRUE) {
640 if (is_openssl_callback) {
641#ifdef USE_OPENSSL
642 /* check certificate with OpenSSL functions, curl has been built against OpenSSL
643 * and we actually have OpenSSL in the monitoring tools
644 */
645 result = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit);
646 return result;
647#else /* USE_OPENSSL */
648 die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL callback used and not linked against OpenSSL\n");
649#endif /* USE_OPENSSL */
650 } else {
651 int i;
652 struct curl_slist *slist;
653
654 cert_ptr.to_info = NULL;
655 res = curl_easy_getinfo (curl, CURLINFO_CERTINFO, &cert_ptr.to_info);
656 if (!res && cert_ptr.to_info) {
657#ifdef USE_OPENSSL
658 /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert parsing
659 * We only check the first certificate and assume it's the one of the server
660 */
661 const char* raw_cert = NULL;
662 for (i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) {
663 for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) {
664 if (verbose >= 2)
665 printf ("%d ** %s\n", i, slist->data);
666 if (strncmp (slist->data, "Cert:", 5) == 0) {
667 raw_cert = &slist->data[5];
668 goto GOT_FIRST_CERT;
669 }
670 }
671 }
672GOT_FIRST_CERT:
673 if (!raw_cert) {
674 snprintf (msg, DEFAULT_BUFFER_SIZE, _("Cannot retrieve certificates from CERTINFO information - certificate data was empty"));
675 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
676 }
677 BIO* cert_BIO = BIO_new (BIO_s_mem());
678 BIO_write (cert_BIO, raw_cert, strlen(raw_cert));
679 cert = PEM_read_bio_X509 (cert_BIO, NULL, NULL, NULL);
680 if (!cert) {
681 snprintf (msg, DEFAULT_BUFFER_SIZE, _("Cannot read certificate from CERTINFO information - BIO error"));
682 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
683 }
684 BIO_free (cert_BIO);
685 result = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit);
686 return result;
687#else /* USE_OPENSSL */
688 /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our disposal,
689 * so we use the libcurl CURLINFO data
690 */
691 result = net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn, days_till_exp_crit);
692 return result;
693#endif /* USE_OPENSSL */
694 } else {
695 snprintf (msg, DEFAULT_BUFFER_SIZE, _("Cannot retrieve certificates - cURL returned %d - %s"),
696 res, curl_easy_strerror(res));
697 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
698 }
699 }
700 }
701 }
702#endif /* LIBCURL_FEATURE_SSL */
703
704 /* we got the data and we executed the request in a given time, so we can append
705 * performance data to the answer always
706 */
707 handle_curl_option_return_code (curl_easy_getinfo (curl, CURLINFO_TOTAL_TIME, &total_time), "CURLINFO_TOTAL_TIME");
708 page_len = get_content_length(&header_buf, &body_buf);
709 if(show_extended_perfdata) {
710 handle_curl_option_return_code (curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &time_connect), "CURLINFO_CONNECT_TIME");
711 handle_curl_option_return_code (curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), "CURLINFO_APPCONNECT_TIME");
712 handle_curl_option_return_code (curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, &time_headers), "CURLINFO_PRETRANSFER_TIME");
713 handle_curl_option_return_code (curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), "CURLINFO_STARTTRANSFER_TIME");
714 snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s",
715 perfd_time(total_time),
716 perfd_size(page_len),
717 perfd_time_connect(time_connect),
718 use_ssl == TRUE ? perfd_time_ssl (time_appconnect-time_connect) : "",
719 perfd_time_headers(time_headers - time_appconnect),
720 perfd_time_firstbyte(time_firstbyte - time_headers),
721 perfd_time_transfer(total_time-time_firstbyte)
722 );
723 } else {
724 snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s",
725 perfd_time(total_time),
726 perfd_size(page_len)
727 );
728 }
729
730 /* return a CRITICAL status if we couldn't read any data */
731 if (strlen(header_buf.buf) == 0 && strlen(body_buf.buf) == 0)
732 die (STATE_CRITICAL, _("HTTP CRITICAL - No header received from host\n"));
733
734 /* get status line of answer, check sanity of HTTP code */
735 if (curlhelp_parse_statusline (header_buf.buf, &status_line) < 0) {
736 snprintf (msg, DEFAULT_BUFFER_SIZE, "Unparsable status line in %.3g seconds response time|%s\n",
737 total_time, perfstring);
738 die (STATE_CRITICAL, "HTTP CRITICAL HTTP/1.x %ld unknown - %s", code, msg);
739 }
740
741 /* get result code from cURL */
742 handle_curl_option_return_code (curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &code), "CURLINFO_RESPONSE_CODE");
743 if (verbose>=2)
744 printf ("* curl CURLINFO_RESPONSE_CODE is %ld\n", code);
745
746 /* print status line, header, body if verbose */
747 if (verbose >= 2) {
748 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header_buf.buf,
749 (no_body ? " [[ skipped ]]" : body_buf.buf));
750 }
751
752 /* make sure the status line matches the response we are looking for */
753 if (!expected_statuscode(status_line.first_line, server_expect)) {
754 if (server_port == HTTP_PORT)
755 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), status_line.first_line);
756 else
757 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), server_port, status_line.first_line);
758 die (STATE_CRITICAL, "HTTP CRITICAL - %s", msg);
759 }
760
761 if( server_expect_yn ) {
762 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), server_expect);
763 if (verbose)
764 printf ("%s\n",msg);
765 result = STATE_OK;
766 }
767 else {
768 /* illegal return codes result in a critical state */
769 if (code >= 600 || code < 100) {
770 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), status_line.http_code, status_line.msg);
771 /* server errors result in a critical state */
772 } else if (code >= 500) {
773 result = STATE_CRITICAL;
774 /* client errors result in a warning state */
775 } else if (code >= 400) {
776 result = STATE_WARNING;
777 /* check redirected page if specified */
778 } else if (code >= 300) {
779 if (onredirect == STATE_DEPENDENT) {
780 if( followmethod == FOLLOW_LIBCURL ) {
781 code = status_line.http_code;
782 } else {
783 /* old check_http style redirection, if we come
784 * back here, we are in the same status as with
785 * the libcurl method
786 */
787 redir (&header_buf);
788 }
789 } else {
790 /* this is a specific code in the command line to
791 * be returned when a redirection is encoutered
792 */
793 }
794 result = max_state_alt (onredirect, result);
795 /* all other codes are considered ok */
796 } else {
797 result = STATE_OK;
798 }
799 }
800
801 /* libcurl redirection internally, handle error states here */
802 if( followmethod == FOLLOW_LIBCURL ) {
803 handle_curl_option_return_code (curl_easy_getinfo (curl, CURLINFO_REDIRECT_COUNT, &redir_depth), "CURLINFO_REDIRECT_COUNT");
804 if (verbose >= 2)
805 printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth);
806 if (redir_depth > max_depth) {
807 snprintf (msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl",
808 max_depth);
809 die (STATE_WARNING, "HTTP WARNING - %s", msg);
810 }
811 }
812
813 /* check status codes, set exit status accordingly */
814 if( status_line.http_code != code ) {
815 die (STATE_CRITICAL, _("HTTP CRITICAL HTTP/%d.%d %d %s - different HTTP codes (cUrl has %ld)\n"),
816 status_line.http_major, status_line.http_minor,
817 status_line.http_code, status_line.msg, code);
818 }
819
820 if (maximum_age >= 0) {
821 result = max_state_alt(check_document_dates(&header_buf, &msg), result);
822 }
823
824 /* Page and Header content checks go here */
825
826 if (strlen (header_expect)) {
827 if (!strstr (header_buf.buf, header_expect)) {
828 strncpy(&output_header_search[0],header_expect,sizeof(output_header_search));
829 if(output_header_search[sizeof(output_header_search)-1]!='\0') {
830 bcopy("...",&output_header_search[sizeof(output_header_search)-4],4);
831 }
832 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);
833 result = STATE_CRITICAL;
834 }
835 }
836
837 if (strlen (string_expect)) {
838 if (!strstr (body_buf.buf, string_expect)) {
839 strncpy(&output_string_search[0],string_expect,sizeof(output_string_search));
840 if(output_string_search[sizeof(output_string_search)-1]!='\0') {
841 bcopy("...",&output_string_search[sizeof(output_string_search)-4],4);
842 }
843 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);
844 result = STATE_CRITICAL;
845 }
846 }
847
848 if (strlen (regexp)) {
849 errcode = regexec (&preg, body_buf.buf, REGS, pmatch, 0);
850 if ((errcode == 0 && invert_regex == 0) || (errcode == REG_NOMATCH && invert_regex == 1)) {
851 /* OK - No-op to avoid changing the logic around it */
852 result = max_state_alt(STATE_OK, result);
853 }
854 else if ((errcode == REG_NOMATCH && invert_regex == 0) || (errcode == 0 && invert_regex == 1)) {
855 if (invert_regex == 0)
856 snprintf (msg, DEFAULT_BUFFER_SIZE, _("%spattern not found, "), msg);
857 else
858 snprintf (msg, DEFAULT_BUFFER_SIZE, _("%spattern found, "), msg);
859 result = STATE_CRITICAL;
860 }
861 else {
862 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
863 snprintf (msg, DEFAULT_BUFFER_SIZE, _("%sExecute Error: %s, "), msg, errbuf);
864 result = STATE_UNKNOWN;
865 }
866 }
867
868 /* make sure the page is of an appropriate size */
869 if ((max_page_len > 0) && (page_len > max_page_len)) {
870 snprintf (msg, DEFAULT_BUFFER_SIZE, _("%spage size %d too large, "), msg, page_len);
871 result = max_state_alt(STATE_WARNING, result);
872 } else if ((min_page_len > 0) && (page_len < min_page_len)) {
873 snprintf (msg, DEFAULT_BUFFER_SIZE, _("%spage size %d too small, "), msg, page_len);
874 result = max_state_alt(STATE_WARNING, result);
875 }
876
877 /* -w, -c: check warning and critical level */
878 result = max_state_alt(get_status(total_time, thlds), result);
879
880 /* Cut-off trailing characters */
881 if(msg[strlen(msg)-2] == ',')
882 msg[strlen(msg)-2] = '\0';
883 else
884 msg[strlen(msg)-3] = '\0';
885
886 /* TODO: separate _() msg and status code: die (result, "HTTP %s: %s\n", state_text(result), msg); */
887 die (result, "HTTP %s: HTTP/%d.%d %d %s%s%s - %d bytes in %.3f second response time %s|%s\n",
888 state_text(result), status_line.http_major, status_line.http_minor,
889 status_line.http_code, status_line.msg,
890 strlen(msg) > 0 ? " - " : "",
891 msg, page_len, total_time,
892 (display_html ? "</A>" : ""),
893 perfstring);
894
895 /* proper cleanup after die? */
896 curlhelp_free_statusline(&status_line);
897 curl_easy_cleanup (curl);
898 curl_global_cleanup ();
899 curlhelp_freewritebuffer (&body_buf);
900 curlhelp_freewritebuffer (&header_buf);
901 if (!strcmp (http_method, "PUT")) {
902 curlhelp_freereadbuffer (&put_buf);
903 }
904
905 return result;
906}
907
908int
909uri_strcmp (const UriTextRangeA range, const char* s)
910{
911 if (!range.first) return -1;
912 if (range.afterLast - range.first < strlen (s)) return -1;
913 return strncmp (s, range.first, min( range.afterLast - range.first, strlen (s)));
914}
915
916char*
917uri_string (const UriTextRangeA range, char* buf, size_t buflen)
918{
919 if (!range.first) return "(null)";
920 strncpy (buf, range.first, max (buflen, range.afterLast - range.first));
921 buf[max (buflen, range.afterLast - range.first)] = '\0';
922 buf[range.afterLast - range.first] = '\0';
923 return buf;
924}
925
926void
927redir (curlhelp_write_curlbuf* header_buf)
928{
929 char *location = NULL;
930 curlhelp_statusline status_line;
931 struct phr_header headers[255];
932 size_t nof_headers = 255;
933 size_t msglen;
934 char buf[DEFAULT_BUFFER_SIZE];
935 char ipstr[INET_ADDR_MAX_SIZE];
936 int new_port;
937 char *new_host;
938 char *new_url;
939
940 int res = phr_parse_response (header_buf->buf, header_buf->buflen,
941 &status_line.http_minor, &status_line.http_code, &status_line.msg, &msglen,
942 headers, &nof_headers, 0);
943
944 location = get_header_value (headers, nof_headers, "location");
945
946 if (verbose >= 2)
947 printf(_("* Seen redirect location %s\n"), location);
948
949 if (++redir_depth > max_depth)
950 die (STATE_WARNING,
951 _("HTTP WARNING - maximum redirection depth %d exceeded - %s%s\n"),
952 max_depth, location, (display_html ? "</A>" : ""));
953
954 UriParserStateA state;
955 UriUriA uri;
956 state.uri = &uri;
957 if (uriParseUriA (&state, location) != URI_SUCCESS) {
958 if (state.errorCode == URI_ERROR_SYNTAX) {
959 die (STATE_UNKNOWN,
960 _("HTTP UNKNOWN - Could not parse redirect location '%s'%s\n"),
961 location, (display_html ? "</A>" : ""));
962 } else if (state.errorCode == URI_ERROR_MALLOC) {
963 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
964 }
965 }
966
967 if (verbose >= 2) {
968 printf (_("** scheme: %s\n"),
969 uri_string (uri.scheme, buf, DEFAULT_BUFFER_SIZE));
970 printf (_("** host: %s\n"),
971 uri_string (uri.hostText, buf, DEFAULT_BUFFER_SIZE));
972 printf (_("** port: %s\n"),
973 uri_string (uri.portText, buf, DEFAULT_BUFFER_SIZE));
974 if (uri.hostData.ip4) {
975 inet_ntop (AF_INET, uri.hostData.ip4->data, ipstr, sizeof (ipstr));
976 printf (_("** IPv4: %s\n"), ipstr);
977 }
978 if (uri.hostData.ip6) {
979 inet_ntop (AF_INET, uri.hostData.ip6->data, ipstr, sizeof (ipstr));
980 printf (_("** IPv6: %s\n"), ipstr);
981 }
982 if (uri.pathHead) {
983 printf (_("** path: "));
984 const UriPathSegmentA* p = uri.pathHead;
985 for (; p; p = p->next) {
986 printf ("/%s", uri_string (p->text, buf, DEFAULT_BUFFER_SIZE));
987 }
988 puts ("");
989 }
990 if (uri.query.first) {
991 printf (_("** query: %s\n"),
992 uri_string (uri.query, buf, DEFAULT_BUFFER_SIZE));
993 }
994 if (uri.fragment.first) {
995 printf (_("** fragment: %s\n"),
996 uri_string (uri.fragment, buf, DEFAULT_BUFFER_SIZE));
997 }
998 }
999
1000 use_ssl = !uri_strcmp (uri.scheme, "https");
1001
1002 /* we do a sloppy test here only, because uriparser would have failed
1003 * above, if the port would be invalid, we just check for MAX_PORT
1004 */
1005 if (uri.portText.first) {
1006 new_port = atoi (uri_string (uri.portText, buf, DEFAULT_BUFFER_SIZE));
1007 } else {
1008 new_port = HTTP_PORT;
1009 if (use_ssl)
1010 new_port = HTTPS_PORT;
1011 }
1012 if (new_port > MAX_PORT)
1013 die (STATE_UNKNOWN,
1014 _("HTTP UNKNOWN - Redirection to port above %d - %s%s\n"),
1015 MAX_PORT, location, display_html ? "</A>" : "");
1016
1017 /* by RFC 7231 relative URLs in Location should be taken relative to
1018 * the original URL, so wy try to form a new absolute URL here
1019 */
1020 if (!uri.scheme.first && !uri.hostText.first) {
1021 new_host = strdup (host_name ? host_name : server_address);
1022 } else {
1023 new_host = strdup (uri_string (uri.hostText, buf, DEFAULT_BUFFER_SIZE));
1024 }
1025
1026 /* compose new path */
1027 /* TODO: handle fragments and query part of URL */
1028 new_url = (char *)calloc( 1, DEFAULT_BUFFER_SIZE);
1029 if (uri.pathHead) {
1030 const UriPathSegmentA* p = uri.pathHead;
1031 for (; p; p = p->next) {
1032 strncat (new_url, "/", DEFAULT_BUFFER_SIZE);
1033 strncat (new_url, uri_string (p->text, buf, DEFAULT_BUFFER_SIZE), DEFAULT_BUFFER_SIZE);
1034 }
1035 }
1036
1037 if (server_port==new_port &&
1038 !strncmp(server_address, new_host, MAX_IPV4_HOSTLENGTH) &&
1039 (host_name && !strncmp(host_name, new_host, MAX_IPV4_HOSTLENGTH)) &&
1040 !strcmp(server_url, new_url))
1041 die (STATE_WARNING,
1042 _("HTTP WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1043 use_ssl ? "https" : "http", new_host, new_port, new_url, (display_html ? "</A>" : ""));
1044
1045 /* set new values for redirected request */
1046
1047 if (!(followsticky & STICKY_HOST)) {
1048 free (server_address);
1049 server_address = strndup (new_host, MAX_IPV4_HOSTLENGTH);
1050 }
1051 if (!(followsticky & STICKY_PORT)) {
1052 server_port = (unsigned short)new_port;
1053 }
1054
1055 free (host_name);
1056 host_name = strndup (new_host, MAX_IPV4_HOSTLENGTH);
1057
1058 /* reset virtual port */
1059 virtual_port = server_port;
1060
1061 free(new_host);
1062 free (server_url);
1063 server_url = new_url;
1064
1065 uriFreeUriMembersA (&uri);
1066
1067 if (verbose)
1068 printf (_("Redirection to %s://%s:%d%s\n"), use_ssl ? "https" : "http",
1069 host_name ? host_name : server_address, server_port, server_url);
1070
1071 /* TODO: the hash component MUST be taken from the original URL and
1072 * attached to the URL in Location
1073 */
1074
1075 check_http ();
1076}
1077
1078/* check whether a file exists */
1079void
1080test_file (char *path)
1081{
1082 if (access(path, R_OK) == 0)
1083 return;
1084 usage2 (_("file does not exist or is not readable"), path);
1085}
1086
1087int
1088process_arguments (int argc, char **argv)
1089{
1090 char *p;
1091 int c = 1;
1092 char *temp;
1093
1094 enum {
1095 INVERT_REGEX = CHAR_MAX + 1,
1096 SNI_OPTION,
1097 CA_CERT_OPTION,
1098 HTTP_VERSION_OPTION
1099 };
1100
1101 int option = 0;
1102 int got_plus = 0;
1103 static struct option longopts[] = {
1104 STD_LONG_OPTS,
1105 {"link", no_argument, 0, 'L'},
1106 {"nohtml", no_argument, 0, 'n'},
1107 {"ssl", optional_argument, 0, 'S'},
1108 {"sni", no_argument, 0, SNI_OPTION},
1109 {"post", required_argument, 0, 'P'},
1110 {"method", required_argument, 0, 'j'},
1111 {"IP-address", required_argument, 0, 'I'},
1112 {"url", required_argument, 0, 'u'},
1113 {"port", required_argument, 0, 'p'},
1114 {"authorization", required_argument, 0, 'a'},
1115 {"proxy-authorization", required_argument, 0, 'b'},
1116 {"header-string", required_argument, 0, 'd'},
1117 {"string", required_argument, 0, 's'},
1118 {"expect", required_argument, 0, 'e'},
1119 {"regex", required_argument, 0, 'r'},
1120 {"ereg", required_argument, 0, 'r'},
1121 {"eregi", required_argument, 0, 'R'},
1122 {"linespan", no_argument, 0, 'l'},
1123 {"onredirect", required_argument, 0, 'f'},
1124 {"certificate", required_argument, 0, 'C'},
1125 {"client-cert", required_argument, 0, 'J'},
1126 {"private-key", required_argument, 0, 'K'},
1127 {"ca-cert", required_argument, 0, CA_CERT_OPTION},
1128 {"useragent", required_argument, 0, 'A'},
1129 {"header", required_argument, 0, 'k'},
1130 {"no-body", no_argument, 0, 'N'},
1131 {"max-age", required_argument, 0, 'M'},
1132 {"content-type", required_argument, 0, 'T'},
1133 {"pagesize", required_argument, 0, 'm'},
1134 {"invert-regex", no_argument, NULL, INVERT_REGEX},
1135 {"use-ipv4", no_argument, 0, '4'},
1136 {"use-ipv6", no_argument, 0, '6'},
1137 {"extended-perfdata", no_argument, 0, 'E'},
1138 {"http-version", required_argument, 0, HTTP_VERSION_OPTION},
1139 {0, 0, 0, 0}
1140 };
1141
1142 if (argc < 2)
1143 return ERROR;
1144
1145 /* support check_http compatible arguments */
1146 for (c = 1; c < argc; c++) {
1147 if (strcmp ("-to", argv[c]) == 0)
1148 strcpy (argv[c], "-t");
1149 if (strcmp ("-hn", argv[c]) == 0)
1150 strcpy (argv[c], "-H");
1151 if (strcmp ("-wt", argv[c]) == 0)
1152 strcpy (argv[c], "-w");
1153 if (strcmp ("-ct", argv[c]) == 0)
1154 strcpy (argv[c], "-c");
1155 if (strcmp ("-nohtml", argv[c]) == 0)
1156 strcpy (argv[c], "-n");
1157 }
1158
1159 server_url = strdup(DEFAULT_SERVER_URL);
1160
1161 while (1) {
1162 c = getopt_long (argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:nlLS::m:M:NE", longopts, &option);
1163 if (c == -1 || c == EOF || c == 1)
1164 break;
1165
1166 switch (c) {
1167 case 'h':
1168 print_help();
1169 exit(STATE_UNKNOWN);
1170 break;
1171 case 'V':
1172 print_revision(progname, NP_VERSION);
1173 print_curl_version();
1174 exit(STATE_UNKNOWN);
1175 break;
1176 case 'v':
1177 verbose++;
1178 break;
1179 case 't': /* timeout period */
1180 if (!is_intnonneg (optarg))
1181 usage2 (_("Timeout interval must be a positive integer"), optarg);
1182 else
1183 socket_timeout = (int)strtol (optarg, NULL, 10);
1184 break;
1185 case 'c': /* critical time threshold */
1186 critical_thresholds = optarg;
1187 break;
1188 case 'w': /* warning time threshold */
1189 warning_thresholds = optarg;
1190 break;
1191 case 'H': /* virtual host */
1192 host_name = strdup (optarg);
1193 if (host_name[0] == '[') {
1194 if ((p = strstr (host_name, "]:")) != NULL) { /* [IPv6]:port */
1195 virtual_port = atoi (p + 2);
1196 /* cut off the port */
1197 host_name_length = strlen (host_name) - strlen (p) - 1;
1198 free (host_name);
1199 host_name = strndup (optarg, host_name_length);
1200 }
1201 } else if ((p = strchr (host_name, ':')) != NULL
1202 && strchr (++p, ':') == NULL) { /* IPv4:port or host:port */
1203 virtual_port = atoi (p);
1204 /* cut off the port */
1205 host_name_length = strlen (host_name) - strlen (p) - 1;
1206 free (host_name);
1207 host_name = strndup (optarg, host_name_length);
1208 }
1209 break;
1210 case 'I': /* internet address */
1211 server_address = strdup (optarg);
1212 break;
1213 case 'u': /* URL path */
1214 server_url = strdup (optarg);
1215 break;
1216 case 'p': /* Server port */
1217 if (!is_intnonneg (optarg))
1218 usage2 (_("Invalid port number, expecting a non-negative number"), optarg);
1219 else {
1220 if( strtol(optarg, NULL, 10) > MAX_PORT)
1221 usage2 (_("Invalid port number, supplied port number is too big"), optarg);
1222 server_port = (unsigned short)strtol(optarg, NULL, 10);
1223 specify_port = TRUE;
1224 }
1225 break;
1226 case 'a': /* authorization info */
1227 strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
1228 user_auth[MAX_INPUT_BUFFER - 1] = 0;
1229 break;
1230 case 'b': /* proxy-authorization info */
1231 strncpy (proxy_auth, optarg, MAX_INPUT_BUFFER - 1);
1232 proxy_auth[MAX_INPUT_BUFFER - 1] = 0;
1233 break;
1234 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
1235 if (! http_post_data)
1236 http_post_data = strdup (optarg);
1237 if (! http_method)
1238 http_method = strdup("POST");
1239 break;
1240 case 'j': /* Set HTTP method */
1241 if (http_method)
1242 free(http_method);
1243 http_method = strdup (optarg);
1244 break;
1245 case 'A': /* useragent */
1246 strncpy (user_agent, optarg, DEFAULT_BUFFER_SIZE);
1247 user_agent[DEFAULT_BUFFER_SIZE-1] = '\0';
1248 break;
1249 case 'k': /* Additional headers */
1250 if (http_opt_headers_count == 0)
1251 http_opt_headers = malloc (sizeof (char *) * (++http_opt_headers_count));
1252 else
1253 http_opt_headers = realloc (http_opt_headers, sizeof (char *) * (++http_opt_headers_count));
1254 http_opt_headers[http_opt_headers_count - 1] = optarg;
1255 break;
1256 case 'L': /* show html link */
1257 display_html = TRUE;
1258 break;
1259 case 'n': /* do not show html link */
1260 display_html = FALSE;
1261 break;
1262 case 'C': /* Check SSL cert validity */
1263#ifdef LIBCURL_FEATURE_SSL
1264 if ((temp=strchr(optarg,','))!=NULL) {
1265 *temp='\0';
1266 if (!is_intnonneg (optarg))
1267 usage2 (_("Invalid certificate expiration period"), optarg);
1268 days_till_exp_warn = atoi(optarg);
1269 *temp=',';
1270 temp++;
1271 if (!is_intnonneg (temp))
1272 usage2 (_("Invalid certificate expiration period"), temp);
1273 days_till_exp_crit = atoi (temp);
1274 }
1275 else {
1276 days_till_exp_crit=0;
1277 if (!is_intnonneg (optarg))
1278 usage2 (_("Invalid certificate expiration period"), optarg);
1279 days_till_exp_warn = atoi (optarg);
1280 }
1281 check_cert = TRUE;
1282 goto enable_ssl;
1283#endif
1284 case 'J': /* use client certificate */
1285#ifdef LIBCURL_FEATURE_SSL
1286 test_file(optarg);
1287 client_cert = optarg;
1288 goto enable_ssl;
1289#endif
1290 case 'K': /* use client private key */
1291#ifdef LIBCURL_FEATURE_SSL
1292 test_file(optarg);
1293 client_privkey = optarg;
1294 goto enable_ssl;
1295#endif
1296#ifdef LIBCURL_FEATURE_SSL
1297 case CA_CERT_OPTION: /* use CA chain file */
1298 test_file(optarg);
1299 ca_cert = optarg;
1300 goto enable_ssl;
1301#endif
1302 case 'S': /* use SSL */
1303#ifdef LIBCURL_FEATURE_SSL
1304 enable_ssl:
1305 use_ssl = TRUE;
1306 /* ssl_version initialized to CURL_SSLVERSION_DEFAULT as a default.
1307 * Only set if it's non-zero. This helps when we include multiple
1308 * parameters, like -S and -C combinations */
1309 ssl_version = CURL_SSLVERSION_DEFAULT;
1310 if (c=='S' && optarg != NULL) {
1311 char *plus_ptr = strchr(optarg, '+');
1312 if (plus_ptr) {
1313 got_plus = 1;
1314 *plus_ptr = '\0';
1315 }
1316
1317 if (optarg[0] == '2')
1318 ssl_version = CURL_SSLVERSION_SSLv2;
1319 else if (optarg[0] == '3')
1320 ssl_version = CURL_SSLVERSION_SSLv3;
1321 else if (!strcmp (optarg, "1") || !strcmp (optarg, "1.0"))
1322#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1323 ssl_version = CURL_SSLVERSION_TLSv1_0;
1324#else
1325 ssl_version = CURL_SSLVERSION_DEFAULT;
1326#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1327 else if (!strcmp (optarg, "1.1"))
1328#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1329 ssl_version = CURL_SSLVERSION_TLSv1_1;
1330#else
1331 ssl_version = CURL_SSLVERSION_DEFAULT;
1332#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1333 else if (!strcmp (optarg, "1.2"))
1334#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1335 ssl_version = CURL_SSLVERSION_TLSv1_2;
1336#else
1337 ssl_version = CURL_SSLVERSION_DEFAULT;
1338#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1339 else if (!strcmp (optarg, "1.3"))
1340#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0)
1341 ssl_version = CURL_SSLVERSION_TLSv1_3;
1342#else
1343 ssl_version = CURL_SSLVERSION_DEFAULT;
1344#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */
1345 else
1346 usage4 (_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2 (with optional '+' suffix)"));
1347 }
1348#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0)
1349 if (got_plus) {
1350 switch (ssl_version) {
1351 case CURL_SSLVERSION_TLSv1_3:
1352 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1353 break;
1354 case CURL_SSLVERSION_TLSv1_2:
1355 case CURL_SSLVERSION_TLSv1_1:
1356 case CURL_SSLVERSION_TLSv1_0:
1357 ssl_version |= CURL_SSLVERSION_MAX_DEFAULT;
1358 break;
1359 }
1360 } else {
1361 switch (ssl_version) {
1362 case CURL_SSLVERSION_TLSv1_3:
1363 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1364 break;
1365 case CURL_SSLVERSION_TLSv1_2:
1366 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2;
1367 break;
1368 case CURL_SSLVERSION_TLSv1_1:
1369 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1;
1370 break;
1371 case CURL_SSLVERSION_TLSv1_0:
1372 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0;
1373 break;
1374 }
1375 }
1376#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */
1377 if (verbose >= 2)
1378 printf(_("* Set SSL/TLS version to %d\n"), ssl_version);
1379 if (specify_port == FALSE)
1380 server_port = HTTPS_PORT;
1381 break;
1382#else /* LIBCURL_FEATURE_SSL */
1383 /* -C -J and -K fall through to here without SSL */
1384 usage4 (_("Invalid option - SSL is not available"));
1385 break;
1386 case SNI_OPTION: /* --sni is parsed, but ignored, the default is TRUE with libcurl */
1387 use_sni = TRUE;
1388 break;
1389#endif /* LIBCURL_FEATURE_SSL */
1390 case 'f': /* onredirect */
1391 if (!strcmp (optarg, "ok"))
1392 onredirect = STATE_OK;
1393 else if (!strcmp (optarg, "warning"))
1394 onredirect = STATE_WARNING;
1395 else if (!strcmp (optarg, "critical"))
1396 onredirect = STATE_CRITICAL;
1397 else if (!strcmp (optarg, "unknown"))
1398 onredirect = STATE_UNKNOWN;
1399 else if (!strcmp (optarg, "follow"))
1400 onredirect = STATE_DEPENDENT;
1401 else if (!strcmp (optarg, "stickyport"))
1402 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST|STICKY_PORT;
1403 else if (!strcmp (optarg, "sticky"))
1404 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST;
1405 else if (!strcmp (optarg, "follow"))
1406 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_NONE;
1407 else if (!strcmp (optarg, "curl"))
1408 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_LIBCURL;
1409 else usage2 (_("Invalid onredirect option"), optarg);
1410 if (verbose >= 2)
1411 printf(_("* Following redirects set to %s\n"), state_text(onredirect));
1412 break;
1413 case 'd': /* string or substring */
1414 strncpy (header_expect, optarg, MAX_INPUT_BUFFER - 1);
1415 header_expect[MAX_INPUT_BUFFER - 1] = 0;
1416 break;
1417 case 's': /* string or substring */
1418 strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
1419 string_expect[MAX_INPUT_BUFFER - 1] = 0;
1420 break;
1421 case 'e': /* string or substring */
1422 strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
1423 server_expect[MAX_INPUT_BUFFER - 1] = 0;
1424 server_expect_yn = 1;
1425 break;
1426 case 'T': /* Content-type */
1427 http_content_type = strdup (optarg);
1428 break;
1429 case 'l': /* linespan */
1430 cflags &= ~REG_NEWLINE;
1431 break;
1432 case 'R': /* regex */
1433 cflags |= REG_ICASE;
1434 case 'r': /* regex */
1435 strncpy (regexp, optarg, MAX_RE_SIZE - 1);
1436 regexp[MAX_RE_SIZE - 1] = 0;
1437 errcode = regcomp (&preg, regexp, cflags);
1438 if (errcode != 0) {
1439 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1440 printf (_("Could Not Compile Regular Expression: %s"), errbuf);
1441 return ERROR;
1442 }
1443 break;
1444 case INVERT_REGEX:
1445 invert_regex = 1;
1446 break;
1447 case '4':
1448 address_family = AF_INET;
1449 break;
1450 case '6':
1451#if defined (USE_IPV6) && defined (LIBCURL_FEATURE_IPV6)
1452 address_family = AF_INET6;
1453#else
1454 usage4 (_("IPv6 support not available"));
1455#endif
1456 break;
1457 case 'm': /* min_page_length */
1458 {
1459 char *tmp;
1460 if (strchr(optarg, ':') != (char *)NULL) {
1461 /* range, so get two values, min:max */
1462 tmp = strtok(optarg, ":");
1463 if (tmp == NULL) {
1464 printf("Bad format: try \"-m min:max\"\n");
1465 exit (STATE_WARNING);
1466 } else
1467 min_page_len = atoi(tmp);
1468
1469 tmp = strtok(NULL, ":");
1470 if (tmp == NULL) {
1471 printf("Bad format: try \"-m min:max\"\n");
1472 exit (STATE_WARNING);
1473 } else
1474 max_page_len = atoi(tmp);
1475 } else
1476 min_page_len = atoi (optarg);
1477 break;
1478 }
1479 case 'N': /* no-body */
1480 no_body = TRUE;
1481 break;
1482 case 'M': /* max-age */
1483 {
1484 int L = strlen(optarg);
1485 if (L && optarg[L-1] == 'm')
1486 maximum_age = atoi (optarg) * 60;
1487 else if (L && optarg[L-1] == 'h')
1488 maximum_age = atoi (optarg) * 60 * 60;
1489 else if (L && optarg[L-1] == 'd')
1490 maximum_age = atoi (optarg) * 60 * 60 * 24;
1491 else if (L && (optarg[L-1] == 's' ||
1492 isdigit (optarg[L-1])))
1493 maximum_age = atoi (optarg);
1494 else {
1495 fprintf (stderr, "unparsable max-age: %s\n", optarg);
1496 exit (STATE_WARNING);
1497 }
1498 if (verbose >= 2)
1499 printf ("* Maximal age of document set to %d seconds\n", maximum_age);
1500 }
1501 break;
1502 case 'E': /* show extended perfdata */
1503 show_extended_perfdata = TRUE;
1504 break;
1505 case HTTP_VERSION_OPTION:
1506 curl_http_version = CURL_HTTP_VERSION_NONE;
1507 if (strcmp (optarg, "1.0") == 0) {
1508 curl_http_version = CURL_HTTP_VERSION_1_0;
1509 } else if (strcmp (optarg, "1.1") == 0) {
1510 curl_http_version = CURL_HTTP_VERSION_1_1;
1511 } else if ((strcmp (optarg, "2.0") == 0) || (strcmp (optarg, "2") == 0)) {
1512#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0)
1513 curl_http_version = CURL_HTTP_VERSION_2_0;
1514#else
1515 curl_http_version = CURL_HTTP_VERSION_NONE;
1516#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) */
1517 } else {
1518 fprintf (stderr, "unkown http-version parameter: %s\n", optarg);
1519 exit (STATE_WARNING);
1520 }
1521 break;
1522 case '?':
1523 /* print short usage statement if args not parsable */
1524 usage5 ();
1525 break;
1526 }
1527 }
1528
1529 c = optind;
1530
1531 if (server_address == NULL && c < argc)
1532 server_address = strdup (argv[c++]);
1533
1534 if (host_name == NULL && c < argc)
1535 host_name = strdup (argv[c++]);
1536
1537 if (server_address == NULL) {
1538 if (host_name == NULL)
1539 usage4 (_("You must specify a server address or host name"));
1540 else
1541 server_address = strdup (host_name);
1542 }
1543
1544 set_thresholds(&thlds, warning_thresholds, critical_thresholds);
1545
1546 if (critical_thresholds && thlds->critical->end>(double)socket_timeout)
1547 socket_timeout = (int)thlds->critical->end + 1;
1548 if (verbose >= 2)
1549 printf ("* Socket timeout set to %ld seconds\n", socket_timeout);
1550
1551 if (http_method == NULL)
1552 http_method = strdup ("GET");
1553
1554 if (client_cert && !client_privkey)
1555 usage4 (_("If you use a client certificate you must also specify a private key file"));
1556
1557 if (virtual_port == 0)
1558 virtual_port = server_port;
1559 else {
1560 if ((use_ssl && server_port == HTTPS_PORT) || (!use_ssl && server_port == HTTP_PORT))
1561 if(specify_port == FALSE)
1562 server_port = virtual_port;
1563 }
1564
1565 return TRUE;
1566}
1567
1568char *perfd_time (double elapsed_time)
1569{
1570 return fperfdata ("time", elapsed_time, "s",
1571 thlds->warning?TRUE:FALSE, thlds->warning?thlds->warning->end:0,
1572 thlds->critical?TRUE:FALSE, thlds->critical?thlds->critical->end:0,
1573 TRUE, 0, TRUE, socket_timeout);
1574}
1575
1576char *perfd_time_connect (double elapsed_time_connect)
1577{
1578 return fperfdata ("time_connect", elapsed_time_connect, "s", FALSE, 0, FALSE, 0, FALSE, 0, TRUE, socket_timeout);
1579}
1580
1581char *perfd_time_ssl (double elapsed_time_ssl)
1582{
1583 return fperfdata ("time_ssl", elapsed_time_ssl, "s", FALSE, 0, FALSE, 0, FALSE, 0, TRUE, socket_timeout);
1584}
1585
1586char *perfd_time_headers (double elapsed_time_headers)
1587{
1588 return fperfdata ("time_headers", elapsed_time_headers, "s", FALSE, 0, FALSE, 0, FALSE, 0, TRUE, socket_timeout);
1589}
1590
1591char *perfd_time_firstbyte (double elapsed_time_firstbyte)
1592{
1593 return fperfdata ("time_firstbyte", elapsed_time_firstbyte, "s", FALSE, 0, FALSE, 0, FALSE, 0, TRUE, socket_timeout);
1594}
1595
1596char *perfd_time_transfer (double elapsed_time_transfer)
1597{
1598 return fperfdata ("time_transfer", elapsed_time_transfer, "s", FALSE, 0, FALSE, 0, FALSE, 0, TRUE, socket_timeout);
1599}
1600
1601char *perfd_size (int page_len)
1602{
1603 return perfdata ("size", page_len, "B",
1604 (min_page_len>0?TRUE:FALSE), min_page_len,
1605 (min_page_len>0?TRUE:FALSE), 0,
1606 TRUE, 0, FALSE, 0);
1607}
1608
1609void
1610print_help (void)
1611{
1612 print_revision (progname, NP_VERSION);
1613
1614 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1615 printf (COPYRIGHT, copyright, email);
1616
1617 printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
1618 printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
1619 printf ("%s\n", _("strings and regular expressions, check connection times, and report on"));
1620 printf ("%s\n", _("certificate expiration times."));
1621 printf ("\n");
1622 printf ("%s\n", _("It makes use of libcurl to do so. It tries to be as compatible to check_http"));
1623 printf ("%s\n", _("as possible."));
1624
1625 printf ("\n\n");
1626
1627 print_usage ();
1628
1629 printf (_("NOTE: One or both of -H and -I must be specified"));
1630
1631 printf ("\n");
1632
1633 printf (UT_HELP_VRSN);
1634 printf (UT_EXTRA_OPTS);
1635
1636 printf (" %s\n", "-H, --hostname=ADDRESS");
1637 printf (" %s\n", _("Host name argument for servers using host headers (virtual host)"));
1638 printf (" %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1639 printf (" %s\n", "-I, --IP-address=ADDRESS");
1640 printf (" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1641 printf (" %s\n", "-p, --port=INTEGER");
1642 printf (" %s", _("Port number (default: "));
1643 printf ("%d)\n", HTTP_PORT);
1644
1645 printf (UT_IPv46);
1646
1647#ifdef LIBCURL_FEATURE_SSL
1648 printf (" %s\n", "-S, --ssl=VERSION[+]");
1649 printf (" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents"));
1650 printf (" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,"));
1651 printf (" %s\n", _("1.2 = TLSv1.2). With a '+' suffix, newer versions are also accepted."));
1652 printf (" %s\n", _("Note: SSLv2 and SSLv3 are deprecated and are usually disabled in libcurl"));
1653 printf (" %s\n", "--sni");
1654 printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
1655#if LIBCURL_VERSION_NUM >= 0x071801
1656 printf (" %s\n", _("Note: --sni is the default in libcurl as SSLv2 and SSLV3 are deprecated and"));
1657 printf (" %s\n", _(" SNI only really works since TLSv1.0"));
1658#else
1659 printf (" %s\n", _("Note: SNI is not supported in libcurl before 7.18.1"));
1660#endif
1661 printf (" %s\n", "-C, --certificate=INTEGER[,INTEGER]");
1662 printf (" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
1663 printf (" %s\n", _("(when this option is used the URL is not checked.)"));
1664 printf (" %s\n", "-J, --client-cert=FILE");
1665 printf (" %s\n", _("Name of file that contains the client certificate (PEM format)"));
1666 printf (" %s\n", _("to be used in establishing the SSL session"));
1667 printf (" %s\n", "-K, --private-key=FILE");
1668 printf (" %s\n", _("Name of file containing the private key (PEM format)"));
1669 printf (" %s\n", _("matching the client certificate"));
1670 printf (" %s\n", "--ca-cert=FILE");
1671 printf (" %s\n", _("CA certificate file to verify peer against"));
1672#endif
1673
1674 printf (" %s\n", "-e, --expect=STRING");
1675 printf (" %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1676 printf (" %s", _("the first (status) line of the server response (default: "));
1677 printf ("%s)\n", HTTP_EXPECT);
1678 printf (" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1679 printf (" %s\n", "-d, --header-string=STRING");
1680 printf (" %s\n", _("String to expect in the response headers"));
1681 printf (" %s\n", "-s, --string=STRING");
1682 printf (" %s\n", _("String to expect in the content"));
1683 printf (" %s\n", "-u, --url=PATH");
1684 printf (" %s\n", _("URL to GET or POST (default: /)"));
1685 printf (" %s\n", "-P, --post=STRING");
1686 printf (" %s\n", _("URL encoded http POST data"));
1687 printf (" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT)");
1688 printf (" %s\n", _("Set HTTP method."));
1689 printf (" %s\n", "-N, --no-body");
1690 printf (" %s\n", _("Don't wait for document body: stop reading after headers."));
1691 printf (" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
1692 printf (" %s\n", "-M, --max-age=SECONDS");
1693 printf (" %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
1694 printf (" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1695 printf (" %s\n", "-T, --content-type=STRING");
1696 printf (" %s\n", _("specify Content-Type header media type when POSTing\n"));
1697 printf (" %s\n", "-l, --linespan");
1698 printf (" %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1699 printf (" %s\n", "-r, --regex, --ereg=STRING");
1700 printf (" %s\n", _("Search page for regex STRING"));
1701 printf (" %s\n", "-R, --eregi=STRING");
1702 printf (" %s\n", _("Search page for case-insensitive regex STRING"));
1703 printf (" %s\n", "--invert-regex");
1704 printf (" %s\n", _("Return CRITICAL if found, OK if not\n"));
1705 printf (" %s\n", "-a, --authorization=AUTH_PAIR");
1706 printf (" %s\n", _("Username:password on sites with basic authentication"));
1707 printf (" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
1708 printf (" %s\n", _("Username:password on proxy-servers with basic authentication"));
1709 printf (" %s\n", "-A, --useragent=STRING");
1710 printf (" %s\n", _("String to be sent in http header as \"User Agent\""));
1711 printf (" %s\n", "-k, --header=STRING");
1712 printf (" %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers"));
1713 printf (" %s\n", "-E, --extended-perfdata");
1714 printf (" %s\n", _("Print additional performance data"));
1715 printf (" %s\n", "-L, --link");
1716 printf (" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1717 printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport|curl>");
1718 printf (" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1719 printf (" %s\n", _("specified IP address. stickyport also ensures port stays the same."));
1720 printf (" %s\n", _("follow uses the old redirection algorithm of check_http."));
1721 printf (" %s\n", _("curl uses CURL_FOLLOWLOCATION built into libcurl."));
1722 printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1723 printf (" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1724 printf ("\n");
1725 printf (" %s\n", "--http-version=VERSION");
1726 printf (" %s\n", _("Connect via specific HTTP protocol."));
1727 printf (" %s\n", _("1.0 = HTTP/1.0, 1.1 = HTTP/1.1, 2.0 = HTTP/2 (HTTP/2 will fail without -S)"));
1728 printf ("\n");
1729
1730 printf (UT_WARN_CRIT);
1731
1732 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1733
1734 printf (UT_VERBOSE);
1735
1736 printf ("\n");
1737 printf ("%s\n", _("Notes:"));
1738 printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1739 printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1740 printf (" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response"));
1741 printf (" %s\n", _("messages from the host result in STATE_WARNING return values. If you are"));
1742 printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1743 printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
1744
1745#ifdef LIBCURL_FEATURE_SSL
1746 printf ("\n");
1747 printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
1748 printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
1749 printf (" %s\n", _("certificate is still valid for the specified number of days."));
1750 printf ("\n");
1751 printf (" %s\n", _("Please note that this plugin does not check if the presented server"));
1752 printf (" %s\n", _("certificate matches the hostname of the server, or if the certificate"));
1753 printf (" %s\n", _("has a valid chain of trust to one of the locally installed CAs."));
1754 printf ("\n");
1755 printf ("%s\n", _("Examples:"));
1756 printf (" %s\n\n", "CHECK CONTENT: check_curl -w 5 -c 10 --ssl -H www.verisign.com");
1757 printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
1758 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1759 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1760 printf (" %s\n", _("a STATE_CRITICAL will be returned."));
1761 printf ("\n");
1762 printf (" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 14");
1763 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1764 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1765 printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
1766 printf (" %s\n\n", _("the certificate is expired."));
1767 printf ("\n");
1768 printf (" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 30,14");
1769 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 30 days,"));
1770 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1771 printf (" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned."));
1772 printf (" %s\n", _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days"));
1773#endif
1774
1775 printf ("\n %s\n", "CHECK WEBSERVER CONTENT VIA PROXY:");
1776 printf (" %s\n", _("It is recommended to use an environment proxy like:"));
1777 printf (" %s\n", _("http_proxy=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org"));
1778 printf (" %s\n", _("legacy proxy requests in check_http style still work:"));
1779 printf (" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u http://www.monitoring-plugins.org/ -H www.monitoring-plugins.org"));
1780
1781#ifdef LIBCURL_FEATURE_SSL
1782 printf ("\n %s\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: ");
1783 printf (" %s\n", _("It is recommended to use an environment proxy like:"));
1784 printf (" %s\n", _("https_proxy=http://192.168.100.35:3128 ./check_curl -H www.verisign.com -S"));
1785 printf (" %s\n", _("legacy proxy requests in check_http style still work:"));
1786 printf (" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u https://www.verisign.com/ -S -j CONNECT -H www.verisign.com "));
1787 printf (" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> -S(sl) -j CONNECT -H <webserver>"));
1788 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1789 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1790 printf (" %s\n", _("a STATE_CRITICAL will be returned."));
1791
1792#endif
1793
1794 printf (UT_SUPPORT);
1795
1796}
1797
1798
1799
1800void
1801print_usage (void)
1802{
1803 printf ("%s\n", _("Usage:"));
1804 printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname);
1805 printf (" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate file>]\n");
1806 printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n");
1807 printf (" [-b proxy_auth] [-f <ok|warning|critcal|follow|sticky|stickyport|curl>]\n");
1808 printf (" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
1809 printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
1810 printf (" [-A string] [-k string] [-S <version>] [--sni] [-C <warn_age>[,<crit_age>]]\n");
1811 printf (" [-T <content-type>] [-j method]\n");
1812 printf (" [--http-version=<version>]\n");
1813 printf ("\n");
1814 printf ("%s\n", _("WARNING: check_curl is experimental. Please use"));
1815 printf ("%s\n\n", _("check_http if you need a stable version."));
1816}
1817
1818void
1819print_curl_version (void)
1820{
1821 printf( "%s\n", curl_version());
1822}
1823
1824int
1825curlhelp_initwritebuffer (curlhelp_write_curlbuf *buf)
1826{
1827 buf->bufsize = DEFAULT_BUFFER_SIZE;
1828 buf->buflen = 0;
1829 buf->buf = (char *)malloc ((size_t)buf->bufsize);
1830 if (buf->buf == NULL) return -1;
1831 return 0;
1832}
1833
1834int
1835curlhelp_buffer_write_callback (void *buffer, size_t size, size_t nmemb, void *stream)
1836{
1837 curlhelp_write_curlbuf *buf = (curlhelp_write_curlbuf *)stream;
1838
1839 while (buf->bufsize < buf->buflen + size * nmemb + 1) {
1840 buf->bufsize *= buf->bufsize * 2;
1841 buf->buf = (char *)realloc (buf->buf, buf->bufsize);
1842 if (buf->buf == NULL) return -1;
1843 }
1844
1845 memcpy (buf->buf + buf->buflen, buffer, size * nmemb);
1846 buf->buflen += size * nmemb;
1847 buf->buf[buf->buflen] = '\0';
1848
1849 return (int)(size * nmemb);
1850}
1851
1852int
1853curlhelp_buffer_read_callback (void *buffer, size_t size, size_t nmemb, void *stream)
1854{
1855 curlhelp_read_curlbuf *buf = (curlhelp_read_curlbuf *)stream;
1856
1857 size_t n = min (nmemb * size, buf->buflen - buf->pos);
1858
1859 memcpy (buffer, buf->buf + buf->pos, n);
1860 buf->pos += n;
1861
1862 return (int)n;
1863}
1864
1865void
1866curlhelp_freewritebuffer (curlhelp_write_curlbuf *buf)
1867{
1868 free (buf->buf);
1869 buf->buf = NULL;
1870}
1871
1872int
1873curlhelp_initreadbuffer (curlhelp_read_curlbuf *buf, const char *data, size_t datalen)
1874{
1875 buf->buflen = datalen;
1876 buf->buf = (char *)malloc ((size_t)buf->buflen);
1877 if (buf->buf == NULL) return -1;
1878 memcpy (buf->buf, data, datalen);
1879 buf->pos = 0;
1880 return 0;
1881}
1882
1883void
1884curlhelp_freereadbuffer (curlhelp_read_curlbuf *buf)
1885{
1886 free (buf->buf);
1887 buf->buf = NULL;
1888}
1889
1890/* TODO: where to put this, it's actually part of sstrings2 (logically)?
1891 */
1892const char*
1893strrstr2(const char *haystack, const char *needle)
1894{
1895 int counter;
1896 size_t len;
1897 const char *prev_pos;
1898 const char *pos;
1899
1900 if (haystack == NULL || needle == NULL)
1901 return NULL;
1902
1903 if (haystack[0] == '\0' || needle[0] == '\0')
1904 return NULL;
1905
1906 counter = 0;
1907 prev_pos = NULL;
1908 pos = haystack;
1909 len = strlen (needle);
1910 for (;;) {
1911 pos = strstr (pos, needle);
1912 if (pos == NULL) {
1913 if (counter == 0)
1914 return NULL;
1915 else
1916 return prev_pos;
1917 }
1918 counter++;
1919 prev_pos = pos;
1920 pos += len;
1921 if (*pos == '\0') return prev_pos;
1922 }
1923}
1924
1925int
1926curlhelp_parse_statusline (const char *buf, curlhelp_statusline *status_line)
1927{
1928 char *first_line_end;
1929 char *p;
1930 size_t first_line_len;
1931 char *pp;
1932 const char *start;
1933 char *first_line_buf;
1934
1935 /* find last start of a new header */
1936 start = strrstr2 (buf, "\r\nHTTP");
1937 if (start != NULL) {
1938 start += 2;
1939 buf = start;
1940 }
1941
1942 first_line_end = strstr(buf, "\r\n");
1943 if (first_line_end == NULL) return -1;
1944
1945 first_line_len = (size_t)(first_line_end - buf);
1946 status_line->first_line = (char *)malloc (first_line_len + 1);
1947 if (status_line->first_line == NULL) return -1;
1948 memcpy (status_line->first_line, buf, first_line_len);
1949 status_line->first_line[first_line_len] = '\0';
1950 first_line_buf = strdup( status_line->first_line );
1951
1952 /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */
1953
1954 p = strtok(first_line_buf, "/");
1955 if( p == NULL ) { free( first_line_buf ); return -1; }
1956 if( strcmp( p, "HTTP" ) != 0 ) { free( first_line_buf ); return -1; }
1957
1958 p = strtok( NULL, " " );
1959 if( p == NULL ) { free( first_line_buf ); return -1; }
1960 if( strchr( p, '.' ) != NULL ) {
1961
1962 /* HTTP 1.x case */
1963 char *ppp;
1964 ppp = strtok( p, "." );
1965 status_line->http_major = (int)strtol( p, &pp, 10 );
1966 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
1967 ppp = strtok( NULL, " " );
1968 status_line->http_minor = (int)strtol( p, &pp, 10 );
1969 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
1970 p += 4; /* 1.x SP */
1971 } else {
1972 /* HTTP 2 case */
1973 status_line->http_major = (int)strtol( p, &pp, 10 );
1974 status_line->http_minor = 0;
1975 p += 2; /* 2 SP */
1976 }
1977
1978 /* status code: "404" or "404.1", then SP */
1979
1980 p = strtok( p, " " );
1981 if( p == NULL ) { free( first_line_buf ); return -1; }
1982 if( strchr( p, '.' ) != NULL ) {
1983 char *ppp;
1984 ppp = strtok( p, "." );
1985 status_line->http_code = (int)strtol( ppp, &pp, 10 );
1986 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
1987 ppp = strtok( NULL, "" );
1988 status_line->http_subcode = (int)strtol( ppp, &pp, 10 );
1989 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
1990 p += 6; /* 400.1 SP */
1991 } else {
1992 status_line->http_code = (int)strtol( p, &pp, 10 );
1993 status_line->http_subcode = -1;
1994 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
1995 p += 4; /* 400 SP */
1996 }
1997
1998 /* Human readable message: "Not Found" CRLF */
1999
2000 p = strtok( p, "" );
2001 if( p == NULL ) { status_line->msg = ""; return 0; }
2002 status_line->msg = status_line->first_line + ( p - first_line_buf );
2003 free( first_line_buf );
2004
2005 return 0;
2006}
2007
2008void
2009curlhelp_free_statusline (curlhelp_statusline *status_line)
2010{
2011 free (status_line->first_line);
2012}
2013
2014void
2015remove_newlines (char *s)
2016{
2017 char *p;
2018
2019 for (p = s; *p != '\0'; p++)
2020 if (*p == '\r' || *p == '\n')
2021 *p = ' ';
2022}
2023
2024char *
2025get_header_value (const struct phr_header* headers, const size_t nof_headers, const char* header)
2026{
2027 int i;
2028 for( i = 0; i < nof_headers; i++ ) {
2029 if( strncasecmp( header, headers[i].name, max( headers[i].name_len, 4 ) ) == 0 ) {
2030 return strndup( headers[i].value, headers[i].value_len );
2031 }
2032 }
2033 return NULL;
2034}
2035
2036int
2037check_document_dates (const curlhelp_write_curlbuf *header_buf, char (*msg)[DEFAULT_BUFFER_SIZE])
2038{
2039 char *server_date = NULL;
2040 char *document_date = NULL;
2041 int date_result = STATE_OK;
2042 curlhelp_statusline status_line;
2043 struct phr_header headers[255];
2044 size_t nof_headers = 255;
2045 size_t msglen;
2046
2047 int res = phr_parse_response (header_buf->buf, header_buf->buflen,
2048 &status_line.http_minor, &status_line.http_code, &status_line.msg, &msglen,
2049 headers, &nof_headers, 0);
2050
2051 server_date = get_header_value (headers, nof_headers, "date");
2052 document_date = get_header_value (headers, nof_headers, "last-modified");
2053
2054 if (!server_date || !*server_date) {
2055 snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sServer date unknown, "), *msg);
2056 date_result = max_state_alt(STATE_UNKNOWN, date_result);
2057 } else if (!document_date || !*document_date) {
2058 snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sDocument modification date unknown, "), *msg);
2059 date_result = max_state_alt(STATE_CRITICAL, date_result);
2060 } else {
2061 time_t srv_data = curl_getdate (server_date, NULL);
2062 time_t doc_data = curl_getdate (document_date, NULL);
2063 if (verbose >= 2)
2064 printf ("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data, document_date, (int)doc_data);
2065 if (srv_data <= 0) {
2066 snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sServer date \"%100s\" unparsable, "), *msg, server_date);
2067 date_result = max_state_alt(STATE_CRITICAL, date_result);
2068 } else if (doc_data <= 0) {
2069 snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date);
2070 date_result = max_state_alt(STATE_CRITICAL, date_result);
2071 } else if (doc_data > srv_data + 30) {
2072 snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data);
2073 date_result = max_state_alt(STATE_CRITICAL, date_result);
2074 } else if (doc_data < srv_data - maximum_age) {
2075 int n = (srv_data - doc_data);
2076 if (n > (60 * 60 * 24 * 2)) {
2077 snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), *msg, ((float) n) / (60 * 60 * 24));
2078 date_result = max_state_alt(STATE_CRITICAL, date_result);
2079 } else {
2080 snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60);
2081 date_result = max_state_alt(STATE_CRITICAL, date_result);
2082 }
2083 }
2084 }
2085
2086 if (server_date) free (server_date);
2087 if (document_date) free (document_date);
2088
2089 return date_result;
2090}
2091
2092
2093int
2094get_content_length (const curlhelp_write_curlbuf* header_buf, const curlhelp_write_curlbuf* body_buf)
2095{
2096 const char *s;
2097 int content_length = 0;
2098 char *copy;
2099 struct phr_header headers[255];
2100 size_t nof_headers = 255;
2101 size_t msglen;
2102 char *content_length_s = NULL;
2103 curlhelp_statusline status_line;
2104
2105 int res = phr_parse_response (header_buf->buf, header_buf->buflen,
2106 &status_line.http_minor, &status_line.http_code, &status_line.msg, &msglen,
2107 headers, &nof_headers, 0);
2108
2109 content_length_s = get_header_value (headers, nof_headers, "content-length");
2110 if (!content_length_s) {
2111 return header_buf->buflen + body_buf->buflen;
2112 }
2113 content_length_s += strspn (content_length_s, " \t");
2114 content_length = atoi (content_length_s);
2115 if (content_length != body_buf->buflen) {
2116 /* TODO: should we warn if the actual and the reported body length don't match? */
2117 }
2118
2119 if (content_length_s) free (content_length_s);
2120
2121 return header_buf->buflen + body_buf->buflen;
2122}
2123
2124/* TODO: is there a better way in libcurl to check for the SSL library? */
2125curlhelp_ssl_library
2126curlhelp_get_ssl_library (CURL* curl)
2127{
2128 curl_version_info_data* version_data;
2129 char *ssl_version;
2130 char *library;
2131 curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN;
2132
2133 version_data = curl_version_info (CURLVERSION_NOW);
2134 if (version_data == NULL) return CURLHELP_SSL_LIBRARY_UNKNOWN;
2135
2136 ssl_version = strdup (version_data->ssl_version);
2137 if (ssl_version == NULL ) return CURLHELP_SSL_LIBRARY_UNKNOWN;
2138
2139 library = strtok (ssl_version, "/");
2140 if (library == NULL) return CURLHELP_SSL_LIBRARY_UNKNOWN;
2141
2142 if (strcmp (library, "OpenSSL") == 0)
2143 ssl_library = CURLHELP_SSL_LIBRARY_OPENSSL;
2144 else if (strcmp (library, "LibreSSL") == 0)
2145 ssl_library = CURLHELP_SSL_LIBRARY_LIBRESSL;
2146 else if (strcmp (library, "GnuTLS") == 0)
2147 ssl_library = CURLHELP_SSL_LIBRARY_GNUTLS;
2148 else if (strcmp (library, "NSS") == 0)
2149 ssl_library = CURLHELP_SSL_LIBRARY_NSS;
2150
2151 if (verbose >= 2)
2152 printf ("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library, ssl_library);
2153
2154 free (ssl_version);
2155
2156 return ssl_library;
2157}
2158
2159const char*
2160curlhelp_get_ssl_library_string (curlhelp_ssl_library ssl_library)
2161{
2162 switch (ssl_library) {
2163 case CURLHELP_SSL_LIBRARY_OPENSSL:
2164 return "OpenSSL";
2165 case CURLHELP_SSL_LIBRARY_LIBRESSL:
2166 return "LibreSSL";
2167 case CURLHELP_SSL_LIBRARY_GNUTLS:
2168 return "GnuTLS";
2169 case CURLHELP_SSL_LIBRARY_NSS:
2170 return "NSS";
2171 case CURLHELP_SSL_LIBRARY_UNKNOWN:
2172 default:
2173 return "unknown";
2174 }
2175}
2176
2177#ifdef LIBCURL_FEATURE_SSL
2178#ifndef USE_OPENSSL
2179time_t
2180parse_cert_date (const char *s)
2181{
2182 struct tm tm;
2183 time_t date;
2184 char *res;
2185
2186 if (!s) return -1;
2187
2188 /* Jan 17 14:25:12 2020 GMT */
2189 res = strptime (s, "%Y-%m-%d %H:%M:%S GMT", &tm);
2190 /* Sep 11 12:00:00 2020 GMT */
2191 if (res == NULL) strptime (s, "%Y %m %d %H:%M:%S GMT", &tm);
2192 date = mktime (&tm);
2193
2194 return date;
2195}
2196
2197/* TODO: this needs cleanup in the sslutils.c, maybe we the #else case to
2198 * OpenSSL could be this function
2199 */
2200int
2201net_noopenssl_check_certificate (cert_ptr_union* cert_ptr, int days_till_exp_warn, int days_till_exp_crit)
2202{
2203 int i;
2204 struct curl_slist* slist;
2205 int cname_found = 0;
2206 char* start_date_str = NULL;
2207 char* end_date_str = NULL;
2208 time_t start_date;
2209 time_t end_date;
2210 char *tz;
2211 float time_left;
2212 int days_left;
2213 int time_remaining;
2214 char timestamp[50] = "";
2215 int status = STATE_UNKNOWN;
2216
2217 if (verbose >= 2)
2218 printf ("**** REQUEST CERTIFICATES ****\n");
2219
2220 for (i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) {
2221 for (slist = cert_ptr->to_certinfo->certinfo[i]; slist; slist = slist->next) {
2222 /* find first common name in subject,
2223 * TODO: check alternative subjects for
2224 * TODO: have a decent parser here and not a hack
2225 * multi-host certificate, check wildcards
2226 */
2227 if (strncasecmp (slist->data, "Subject:", 8) == 0) {
2228 int d = 3;
2229 char* p = strstr (slist->data, "CN=");
2230 if (p == NULL) {
2231 d = 5;
2232 p = strstr (slist->data, "CN = ");
2233 }
2234 if (p != NULL) {
2235 if (strncmp (host_name, p+d, strlen (host_name)) == 0) {
2236 cname_found = 1;
2237 }
2238 }
2239 } else if (strncasecmp (slist->data, "Start Date:", 11) == 0) {
2240 start_date_str = &slist->data[11];
2241 } else if (strncasecmp (slist->data, "Expire Date:", 12) == 0) {
2242 end_date_str = &slist->data[12];
2243 } else if (strncasecmp (slist->data, "Cert:", 5) == 0) {
2244 goto HAVE_FIRST_CERT;
2245 }
2246 if (verbose >= 2)
2247 printf ("%d ** %s\n", i, slist->data);
2248 }
2249 }
2250HAVE_FIRST_CERT:
2251
2252 if (verbose >= 2)
2253 printf ("**** REQUEST CERTIFICATES ****\n");
2254
2255 if (!cname_found) {
2256 printf("%s\n",_("CRITICAL - Cannot retrieve certificate subject."));
2257 return STATE_CRITICAL;
2258 }
2259
2260 start_date = parse_cert_date (start_date_str);
2261 if (start_date <= 0) {
2262 snprintf (msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Start Date' in certificate: '%s'"),
2263 start_date_str);
2264 puts (msg);
2265 return STATE_WARNING;
2266 }
2267
2268 end_date = parse_cert_date (end_date_str);
2269 if (end_date <= 0) {
2270 snprintf (msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Expire Date' in certificate: '%s'"),
2271 start_date_str);
2272 puts (msg);
2273 return STATE_WARNING;
2274 }
2275
2276 time_left = difftime (end_date, time(NULL));
2277 days_left = time_left / 86400;
2278 tz = getenv("TZ");
2279 setenv("TZ", "GMT", 1);
2280 tzset();
2281 strftime(timestamp, 50, "%c %z", localtime(&end_date));
2282 if (tz)
2283 setenv("TZ", tz, 1);
2284 else
2285 unsetenv("TZ");
2286 tzset();
2287
2288 if (days_left > 0 && days_left <= days_till_exp_warn) {
2289 printf (_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), (days_left>days_till_exp_crit)?"WARNING":"CRITICAL", host_name, days_left, timestamp);
2290 if (days_left > days_till_exp_crit)
2291 status = STATE_WARNING;
2292 else
2293 status = STATE_CRITICAL;
2294 } else if (days_left == 0 && time_left > 0) {
2295 if (time_left >= 3600)
2296 time_remaining = (int) time_left / 3600;
2297 else
2298 time_remaining = (int) time_left / 60;
2299
2300 printf (_("%s - Certificate '%s' expires in %u %s (%s)\n"),
2301 (days_left>days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, time_remaining,
2302 time_left >= 3600 ? "hours" : "minutes", timestamp);
2303
2304 if ( days_left > days_till_exp_crit)
2305 status = STATE_WARNING;
2306 else
2307 status = STATE_CRITICAL;
2308 } else if (time_left < 0) {
2309 printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), host_name, timestamp);
2310 status=STATE_CRITICAL;
2311 } else if (days_left == 0) {
2312 printf (_("%s - Certificate '%s' just expired (%s).\n"), (days_left>days_till_exp_crit)?"WARNING":"CRITICAL", host_name, timestamp);
2313 if (days_left > days_till_exp_crit)
2314 status = STATE_WARNING;
2315 else
2316 status = STATE_CRITICAL;
2317 } else {
2318 printf(_("OK - Certificate '%s' will expire on %s.\n"), host_name, timestamp);
2319 status = STATE_OK;
2320 }
2321 return status;
2322}
2323#endif /* USE_OPENSSL */
2324#endif /* LIBCURL_FEATURE_SSL */
diff --git a/plugins/picohttpparser/Makefile.am b/plugins/picohttpparser/Makefile.am
new file mode 100644
index 0000000..87e0531
--- /dev/null
+++ b/plugins/picohttpparser/Makefile.am
@@ -0,0 +1,3 @@
1noinst_LIBRARIES = libpicohttpparser.a
2
3libpicohttpparser_a_SOURCES = picohttpparser.c picohttpparser.h
diff --git a/plugins/picohttpparser/picohttpparser.c b/plugins/picohttpparser/picohttpparser.c
new file mode 100644
index 0000000..74ccc3e
--- /dev/null
+++ b/plugins/picohttpparser/picohttpparser.c
@@ -0,0 +1,645 @@
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#if __GNUC__ >= 3
40#define likely(x) __builtin_expect(!!(x), 1)
41#define unlikely(x) __builtin_expect(!!(x), 0)
42#else
43#define likely(x) (x)
44#define unlikely(x) (x)
45#endif
46
47#ifdef _MSC_VER
48#define ALIGNED(n) _declspec(align(n))
49#else
50#define ALIGNED(n) __attribute__((aligned(n)))
51#endif
52
53#define IS_PRINTABLE_ASCII(c) ((unsigned char)(c)-040u < 0137u)
54
55#define CHECK_EOF() \
56 if (buf == buf_end) { \
57 *ret = -2; \
58 return NULL; \
59 }
60
61#define EXPECT_CHAR_NO_CHECK(ch) \
62 if (*buf++ != ch) { \
63 *ret = -1; \
64 return NULL; \
65 }
66
67#define EXPECT_CHAR(ch) \
68 CHECK_EOF(); \
69 EXPECT_CHAR_NO_CHECK(ch);
70
71#define ADVANCE_TOKEN(tok, toklen) \
72 do { \
73 const char *tok_start = buf; \
74 static const char ALIGNED(16) ranges2[16] = "\000\040\177\177"; \
75 int found2; \
76 buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \
77 if (!found2) { \
78 CHECK_EOF(); \
79 } \
80 while (1) { \
81 if (*buf == ' ') { \
82 break; \
83 } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \
84 if ((unsigned char)*buf < '\040' || *buf == '\177') { \
85 *ret = -1; \
86 return NULL; \
87 } \
88 } \
89 ++buf; \
90 CHECK_EOF(); \
91 } \
92 tok = tok_start; \
93 toklen = buf - tok_start; \
94 } while (0)
95
96static 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"
97 "\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"
98 "\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"
99 "\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"
100 "\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"
101 "\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"
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
105static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges, size_t ranges_size, int *found)
106{
107 *found = 0;
108#if __SSE4_2__
109 if (likely(buf_end - buf >= 16)) {
110 __m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges);
111
112 size_t left = (buf_end - buf) & ~15;
113 do {
114 __m128i b16 = _mm_loadu_si128((const __m128i *)buf);
115 int r = _mm_cmpestri(ranges16, ranges_size, b16, 16, _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS);
116 if (unlikely(r != 16)) {
117 buf += r;
118 *found = 1;
119 break;
120 }
121 buf += 16;
122 left -= 16;
123 } while (likely(left != 0));
124 }
125#else
126 /* suppress unused parameter warning */
127 (void)buf_end;
128 (void)ranges;
129 (void)ranges_size;
130#endif
131 return buf;
132}
133
134static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token, size_t *token_len, int *ret)
135{
136 const char *token_start = buf;
137
138#ifdef __SSE4_2__
139 static const char ALIGNED(16) ranges1[16] = "\0\010" /* allow HT */
140 "\012\037" /* allow SP and up to but not including DEL */
141 "\177\177"; /* allow chars w. MSB set */
142 int found;
143 buf = findchar_fast(buf, buf_end, ranges1, 6, &found);
144 if (found)
145 goto FOUND_CTL;
146#else
147 /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined */
148 while (likely(buf_end - buf >= 8)) {
149#define DOIT() \
150 do { \
151 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \
152 goto NonPrintable; \
153 ++buf; \
154 } while (0)
155 DOIT();
156 DOIT();
157 DOIT();
158 DOIT();
159 DOIT();
160 DOIT();
161 DOIT();
162 DOIT();
163#undef DOIT
164 continue;
165 NonPrintable:
166 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) {
167 goto FOUND_CTL;
168 }
169 ++buf;
170 }
171#endif
172 for (;; ++buf) {
173 CHECK_EOF();
174 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) {
175 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) {
176 goto FOUND_CTL;
177 }
178 }
179 }
180FOUND_CTL:
181 if (likely(*buf == '\015')) {
182 ++buf;
183 EXPECT_CHAR('\012');
184 *token_len = buf - 2 - token_start;
185 } else if (*buf == '\012') {
186 *token_len = buf - token_start;
187 ++buf;
188 } else {
189 *ret = -1;
190 return NULL;
191 }
192 *token = token_start;
193
194 return buf;
195}
196
197static const char *is_complete(const char *buf, const char *buf_end, size_t last_len, int *ret)
198{
199 int ret_cnt = 0;
200 buf = last_len < 3 ? buf : buf + last_len - 3;
201
202 while (1) {
203 CHECK_EOF();
204 if (*buf == '\015') {
205 ++buf;
206 CHECK_EOF();
207 EXPECT_CHAR('\012');
208 ++ret_cnt;
209 } else if (*buf == '\012') {
210 ++buf;
211 ++ret_cnt;
212 } else {
213 ++buf;
214 ret_cnt = 0;
215 }
216 if (ret_cnt == 2) {
217 return buf;
218 }
219 }
220
221 *ret = -2;
222 return NULL;
223}
224
225#define PARSE_INT(valp_, mul_) \
226 if (*buf < '0' || '9' < *buf) { \
227 buf++; \
228 *ret = -1; \
229 return NULL; \
230 } \
231 *(valp_) = (mul_) * (*buf++ - '0');
232
233#define PARSE_INT_3(valp_) \
234 do { \
235 int res_ = 0; \
236 PARSE_INT(&res_, 100) \
237 *valp_ = res_; \
238 PARSE_INT(&res_, 10) \
239 *valp_ += res_; \
240 PARSE_INT(&res_, 1) \
241 *valp_ += res_; \
242 } while (0)
243
244/* returned pointer is always within [buf, buf_end), or null */
245static const char *parse_http_version(const char *buf, const char *buf_end, int *minor_version, int *ret)
246{
247 /* we want at least [HTTP/1.<two chars>] to try to parse */
248 if (buf_end - buf < 9) {
249 *ret = -2;
250 return NULL;
251 }
252 EXPECT_CHAR_NO_CHECK('H');
253 EXPECT_CHAR_NO_CHECK('T');
254 EXPECT_CHAR_NO_CHECK('T');
255 EXPECT_CHAR_NO_CHECK('P');
256 EXPECT_CHAR_NO_CHECK('/');
257 EXPECT_CHAR_NO_CHECK('1');
258 EXPECT_CHAR_NO_CHECK('.');
259 PARSE_INT(minor_version, 1);
260 return buf;
261}
262
263static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers, size_t *num_headers,
264 size_t max_headers, int *ret)
265{
266 for (;; ++*num_headers) {
267 CHECK_EOF();
268 if (*buf == '\015') {
269 ++buf;
270 EXPECT_CHAR('\012');
271 break;
272 } else if (*buf == '\012') {
273 ++buf;
274 break;
275 }
276 if (*num_headers == max_headers) {
277 *ret = -1;
278 return NULL;
279 }
280 if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) {
281 /* parsing name, but do not discard SP before colon, see
282 * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */
283 headers[*num_headers].name = buf;
284 static const char ALIGNED(16) ranges1[] = "\x00 " /* control chars and up to SP */
285 "\"\"" /* 0x22 */
286 "()" /* 0x28,0x29 */
287 ",," /* 0x2c */
288 "//" /* 0x2f */
289 ":@" /* 0x3a-0x40 */
290 "[]" /* 0x5b-0x5d */
291 "{\377"; /* 0x7b-0xff */
292 int found;
293 buf = findchar_fast(buf, buf_end, ranges1, sizeof(ranges1) - 1, &found);
294 if (!found) {
295 CHECK_EOF();
296 }
297 while (1) {
298 if (*buf == ':') {
299 break;
300 } else if (!token_char_map[(unsigned char)*buf]) {
301 *ret = -1;
302 return NULL;
303 }
304 ++buf;
305 CHECK_EOF();
306 }
307 if ((headers[*num_headers].name_len = buf - headers[*num_headers].name) == 0) {
308 *ret = -1;
309 return NULL;
310 }
311 ++buf;
312 for (;; ++buf) {
313 CHECK_EOF();
314 if (!(*buf == ' ' || *buf == '\t')) {
315 break;
316 }
317 }
318 } else {
319 headers[*num_headers].name = NULL;
320 headers[*num_headers].name_len = 0;
321 }
322 const char *value;
323 size_t value_len;
324 if ((buf = get_token_to_eol(buf, buf_end, &value, &value_len, ret)) == NULL) {
325 return NULL;
326 }
327 /* remove trailing SPs and HTABs */
328 const char *value_end = value + value_len;
329 for (; value_end != value; --value_end) {
330 const char c = *(value_end - 1);
331 if (!(c == ' ' || c == '\t')) {
332 break;
333 }
334 }
335 headers[*num_headers].value = value;
336 headers[*num_headers].value_len = value_end - value;
337 }
338 return buf;
339}
340
341static const char *parse_request(const char *buf, const char *buf_end, const char **method, size_t *method_len, const char **path,
342 size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers,
343 size_t max_headers, int *ret)
344{
345 /* skip first empty line (some clients add CRLF after POST content) */
346 CHECK_EOF();
347 if (*buf == '\015') {
348 ++buf;
349 EXPECT_CHAR('\012');
350 } else if (*buf == '\012') {
351 ++buf;
352 }
353
354 /* parse request line */
355 ADVANCE_TOKEN(*method, *method_len);
356 do {
357 ++buf;
358 } while (*buf == ' ');
359 ADVANCE_TOKEN(*path, *path_len);
360 do {
361 ++buf;
362 } while (*buf == ' ');
363 if (*method_len == 0 || *path_len == 0) {
364 *ret = -1;
365 return NULL;
366 }
367 if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {
368 return NULL;
369 }
370 if (*buf == '\015') {
371 ++buf;
372 EXPECT_CHAR('\012');
373 } else if (*buf == '\012') {
374 ++buf;
375 } else {
376 *ret = -1;
377 return NULL;
378 }
379
380 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
381}
382
383int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len, const char **path,
384 size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len)
385{
386 const char *buf = buf_start, *buf_end = buf_start + len;
387 size_t max_headers = *num_headers;
388 int r;
389
390 *method = NULL;
391 *method_len = 0;
392 *path = NULL;
393 *path_len = 0;
394 *minor_version = -1;
395 *num_headers = 0;
396
397 /* if last_len != 0, check if the request is complete (a fast countermeasure
398 againt slowloris */
399 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
400 return r;
401 }
402
403 if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, minor_version, headers, num_headers, max_headers,
404 &r)) == NULL) {
405 return r;
406 }
407
408 return (int)(buf - buf_start);
409}
410
411static const char *parse_response(const char *buf, const char *buf_end, int *minor_version, int *status, const char **msg,
412 size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t max_headers, int *ret)
413{
414 /* parse "HTTP/1.x" */
415 if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {
416 return NULL;
417 }
418 /* skip space */
419 if (*buf != ' ') {
420 *ret = -1;
421 return NULL;
422 }
423 do {
424 ++buf;
425 } while (*buf == ' ');
426 /* parse status code, we want at least [:digit:][:digit:][:digit:]<other char> to try to parse */
427 if (buf_end - buf < 4) {
428 *ret = -2;
429 return NULL;
430 }
431 PARSE_INT_3(status);
432
433 /* get message includig preceding space */
434 if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) {
435 return NULL;
436 }
437 if (*msg_len == 0) {
438 /* ok */
439 } else if (**msg == ' ') {
440 /* remove preceding space */
441 do {
442 ++*msg;
443 --*msg_len;
444 } while (**msg == ' ');
445 } else {
446 /* garbage found after status code */
447 *ret = -1;
448 return NULL;
449 }
450
451 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
452}
453
454int phr_parse_response(const char *buf_start, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len,
455 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 *minor_version = -1;
462 *status = 0;
463 *msg = NULL;
464 *msg_len = 0;
465 *num_headers = 0;
466
467 /* if last_len != 0, check if the response is complete (a fast countermeasure
468 against slowloris */
469 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
470 return r;
471 }
472
473 if ((buf = parse_response(buf, buf_end, minor_version, status, msg, msg_len, headers, num_headers, max_headers, &r)) == NULL) {
474 return r;
475 }
476
477 return (int)(buf - buf_start);
478}
479
480int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len)
481{
482 const char *buf = buf_start, *buf_end = buf + len;
483 size_t max_headers = *num_headers;
484 int r;
485
486 *num_headers = 0;
487
488 /* if last_len != 0, check if the response is complete (a fast countermeasure
489 against slowloris */
490 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
491 return r;
492 }
493
494 if ((buf = parse_headers(buf, buf_end, headers, num_headers, max_headers, &r)) == NULL) {
495 return r;
496 }
497
498 return (int)(buf - buf_start);
499}
500
501enum {
502 CHUNKED_IN_CHUNK_SIZE,
503 CHUNKED_IN_CHUNK_EXT,
504 CHUNKED_IN_CHUNK_DATA,
505 CHUNKED_IN_CHUNK_CRLF,
506 CHUNKED_IN_TRAILERS_LINE_HEAD,
507 CHUNKED_IN_TRAILERS_LINE_MIDDLE
508};
509
510static int decode_hex(int ch)
511{
512 if ('0' <= ch && ch <= '9') {
513 return ch - '0';
514 } else if ('A' <= ch && ch <= 'F') {
515 return ch - 'A' + 0xa;
516 } else if ('a' <= ch && ch <= 'f') {
517 return ch - 'a' + 0xa;
518 } else {
519 return -1;
520 }
521}
522
523ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *_bufsz)
524{
525 size_t dst = 0, src = 0, bufsz = *_bufsz;
526 ssize_t ret = -2; /* incomplete */
527
528 while (1) {
529 switch (decoder->_state) {
530 case CHUNKED_IN_CHUNK_SIZE:
531 for (;; ++src) {
532 int v;
533 if (src == bufsz)
534 goto Exit;
535 if ((v = decode_hex(buf[src])) == -1) {
536 if (decoder->_hex_count == 0) {
537 ret = -1;
538 goto Exit;
539 }
540 break;
541 }
542 if (decoder->_hex_count == sizeof(size_t) * 2) {
543 ret = -1;
544 goto Exit;
545 }
546 decoder->bytes_left_in_chunk = decoder->bytes_left_in_chunk * 16 + v;
547 ++decoder->_hex_count;
548 }
549 decoder->_hex_count = 0;
550 decoder->_state = CHUNKED_IN_CHUNK_EXT;
551 /* fallthru */
552 case CHUNKED_IN_CHUNK_EXT:
553 /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */
554 for (;; ++src) {
555 if (src == bufsz)
556 goto Exit;
557 if (buf[src] == '\012')
558 break;
559 }
560 ++src;
561 if (decoder->bytes_left_in_chunk == 0) {
562 if (decoder->consume_trailer) {
563 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
564 break;
565 } else {
566 goto Complete;
567 }
568 }
569 decoder->_state = CHUNKED_IN_CHUNK_DATA;
570 /* fallthru */
571 case CHUNKED_IN_CHUNK_DATA: {
572 size_t avail = bufsz - src;
573 if (avail < decoder->bytes_left_in_chunk) {
574 if (dst != src)
575 memmove(buf + dst, buf + src, avail);
576 src += avail;
577 dst += avail;
578 decoder->bytes_left_in_chunk -= avail;
579 goto Exit;
580 }
581 if (dst != src)
582 memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk);
583 src += decoder->bytes_left_in_chunk;
584 dst += decoder->bytes_left_in_chunk;
585 decoder->bytes_left_in_chunk = 0;
586 decoder->_state = CHUNKED_IN_CHUNK_CRLF;
587 }
588 /* fallthru */
589 case CHUNKED_IN_CHUNK_CRLF:
590 for (;; ++src) {
591 if (src == bufsz)
592 goto Exit;
593 if (buf[src] != '\015')
594 break;
595 }
596 if (buf[src] != '\012') {
597 ret = -1;
598 goto Exit;
599 }
600 ++src;
601 decoder->_state = CHUNKED_IN_CHUNK_SIZE;
602 break;
603 case CHUNKED_IN_TRAILERS_LINE_HEAD:
604 for (;; ++src) {
605 if (src == bufsz)
606 goto Exit;
607 if (buf[src] != '\015')
608 break;
609 }
610 if (buf[src++] == '\012')
611 goto Complete;
612 decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE;
613 /* fallthru */
614 case CHUNKED_IN_TRAILERS_LINE_MIDDLE:
615 for (;; ++src) {
616 if (src == bufsz)
617 goto Exit;
618 if (buf[src] == '\012')
619 break;
620 }
621 ++src;
622 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
623 break;
624 default:
625 assert(!"decoder is corrupt");
626 }
627 }
628
629Complete:
630 ret = bufsz - src;
631Exit:
632 if (dst != src)
633 memmove(buf + dst, buf + src, bufsz - src);
634 *_bufsz = dst;
635 return ret;
636}
637
638int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder)
639{
640 return decoder->_state == CHUNKED_IN_CHUNK_DATA;
641}
642
643#undef CHECK_EOF
644#undef EXPECT_CHAR
645#undef ADVANCE_TOKEN
diff --git a/plugins/picohttpparser/picohttpparser.h b/plugins/picohttpparser/picohttpparser.h
new file mode 100644
index 0000000..0849f84
--- /dev/null
+++ b/plugins/picohttpparser/picohttpparser.h
@@ -0,0 +1,87 @@
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#ifdef __cplusplus
37extern "C" {
38#endif
39
40/* contains name and value of a header (name == NULL if is a continuing line
41 * of a multiline header */
42struct phr_header {
43 const char *name;
44 size_t name_len;
45 const char *value;
46 size_t value_len;
47};
48
49/* returns number of bytes consumed if successful, -2 if request is partial,
50 * -1 if failed */
51int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len,
52 int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len);
53
54/* ditto */
55int phr_parse_response(const char *_buf, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len,
56 struct phr_header *headers, size_t *num_headers, size_t last_len);
57
58/* ditto */
59int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len);
60
61/* should be zero-filled before start */
62struct phr_chunked_decoder {
63 size_t bytes_left_in_chunk; /* number of bytes left in current chunk */
64 char consume_trailer; /* if trailing headers should be consumed */
65 char _hex_count;
66 char _state;
67};
68
69/* the function rewrites the buffer given as (buf, bufsz) removing the chunked-
70 * encoding headers. When the function returns without an error, bufsz is
71 * updated to the length of the decoded data available. Applications should
72 * repeatedly call the function while it returns -2 (incomplete) every time
73 * supplying newly arrived data. If the end of the chunked-encoded data is
74 * found, the function returns a non-negative number indicating the number of
75 * octets left undecoded at the tail of the supplied buffer. Returns -1 on
76 * error.
77 */
78ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *bufsz);
79
80/* returns if the chunked decoder is in middle of chunked data */
81int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder);
82
83#ifdef __cplusplus
84}
85#endif
86
87#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..050416c
--- /dev/null
+++ b/plugins/t/check_curl.t
@@ -0,0 +1,216 @@
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 => 57;
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" );
81like( $res->output, '/CURLOPT_URL: http:\/\/'.$host_tcp_http.':80\//ms', "Url OK" );
82
83$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http -p 80");
84like( $res->output, '/^Host: '.$host_tcp_http.'\s*$/ms', "Host Header OK" );
85like( $res->output, '/CURLOPT_URL: http:\/\/'.$host_tcp_http.':80\//ms', "Url OK" );
86
87$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http:8080 -p 80");
88like( $res->output, '/^Host: '.$host_tcp_http.':8080\s*$/ms', "Host Header OK" );
89like( $res->output, '/CURLOPT_URL: http:\/\/'.$host_tcp_http.':80\//ms', "Url OK" );
90
91$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http:8080 -p 80");
92like( $res->output, '/^Host: '.$host_tcp_http.':8080\s*$/ms', "Host Header OK" );
93like( $res->output, '/CURLOPT_URL: http:\/\/'.$host_tcp_http.':80\//ms', "Url OK" );
94
95$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http:8080 -p 80 -k 'Host: testhost:8001'");
96like( $res->output, '/^Host: testhost:8001\s*$/ms', "Host Header OK" );
97like( $res->output, '/CURLOPT_URL: http:\/\/'.$host_tcp_http.':80\//ms', "Url OK" );
98
99$res = NPTest->testCmd("./$plugin -v -I $host_tcp_http -p 80 -k 'Host: testhost:8001'");
100like( $res->output, '/^Host: testhost:8001\s*$/ms', "Host Header OK" );
101like( $res->output, '/CURLOPT_URL: http:\/\/'.$host_tcp_http.':80\//ms', "Url OK" );
102
103SKIP: {
104 skip "No internet access", 3 if $internet_access eq "no";
105
106 $res = NPTest->testCmd("./$plugin -v -H $host_tls_http -S");
107 like( $res->output, '/^Host: '.$host_tls_http.'\s*$/ms', "Host Header OK" );
108
109 $res = NPTest->testCmd("./$plugin -v -H $host_tls_http:8080 -S -p 443");
110 like( $res->output, '/^Host: '.$host_tls_http.':8080\s*$/ms', "Host Header OK" );
111
112 $res = NPTest->testCmd("./$plugin -v -H $host_tls_http:443 -S -p 443");
113 like( $res->output, '/^Host: '.$host_tls_http.'\s*$/ms', "Host Header OK" );
114};
115
116SKIP: {
117 skip "No host serving monitoring in index file", 7 unless $host_tcp_http2;
118
119 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'monitoring'" );
120 cmp_ok( $res->return_code, "==", 0, "Got a reference to 'monitoring'");
121
122 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing'" );
123 cmp_ok( $res->return_code, "==", 2, "Not got 'mONiTORing'");
124 like ( $res->output, "/pattern not found/", "Error message says 'pattern not found'");
125
126 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -R 'mONiTORing'" );
127 cmp_ok( $res->return_code, "==", 0, "But case insensitive doesn't mind 'mONiTORing'");
128
129 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'monitoring' --invert-regex" );
130 cmp_ok( $res->return_code, "==", 2, "Invert results work when found");
131 like ( $res->output, "/pattern found/", "Error message says 'pattern found'");
132
133 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing' --invert-regex" );
134 cmp_ok( $res->return_code, "==", 0, "And also when not found");
135}
136SKIP: {
137 skip "No internet access", 16 if $internet_access eq "no";
138
139 $res = NPTest->testCmd(
140 "./$plugin --ssl $host_tls_http"
141 );
142 cmp_ok( $res->return_code, '==', 0, "Can read https for $host_tls_http" );
143
144 $res = NPTest->testCmd( "./$plugin -C 1 --ssl $host_tls_http" );
145 cmp_ok( $res->return_code, '==', 0, "Checking certificate for $host_tls_http");
146 like ( $res->output, "/Certificate '$host_tls_cert' will expire on/", "Output OK" );
147 my $saved_cert_output = $res->output;
148
149 $res = NPTest->testCmd( "./$plugin -C 8000,1 --ssl $host_tls_http" );
150 cmp_ok( $res->return_code, '==', 1, "Checking certificate for $host_tls_http");
151 like ( $res->output, qr/WARNING - Certificate '$host_tls_cert' expires in \d+ day/, "Output Warning" );
152
153 $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" );
154 is( $res->return_code, 0, "Old syntax for cert checking okay" );
155 is( $res->output, $saved_cert_output, "Same output as new syntax" );
156
157 $res = NPTest->testCmd( "./$plugin -H $host_tls_http -C 1" );
158 is( $res->return_code, 0, "Updated syntax for cert checking okay" );
159 is( $res->output, $saved_cert_output, "Same output as new syntax" );
160
161 $res = NPTest->testCmd( "./$plugin -C 1 $host_tls_http" );
162 cmp_ok( $res->output, 'eq', $saved_cert_output, "--ssl option automatically added");
163
164 $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" );
165 cmp_ok( $res->output, 'eq', $saved_cert_output, "Old syntax for cert checking still works");
166
167 # run some certificate checks with faketime
168 SKIP: {
169 skip "No faketime binary found", 12 if !$faketime;
170 $res = NPTest->testCmd("LC_TIME=C TZ=UTC ./$plugin -C 1 $host_tls_http");
171 like($res->output, qr/OK - Certificate '$host_tls_cert' will expire on/, "Catch cert output");
172 is( $res->return_code, 0, "Catch cert output exit code" );
173 my($mon,$day,$hour,$min,$sec,$year) = ($res->output =~ /(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)/);
174 if(!defined $year) {
175 die("parsing date failed from: ".$res->output);
176 }
177 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};
178 my $ts = mktime($sec, $min, $hour, $day, $months->{$mon}, $year-1900);
179 my $time = strftime("%Y-%m-%d %H:%M:%S", localtime($ts));
180 $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");
181 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' just expired/, "Output on expire date");
182 is( $res->return_code, 2, "Output on expire date" );
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' expires in 0 minutes/, "cert expires in 1 second output");
186 is( $res->return_code, 2, "cert expires in 1 second exit code" );
187
188 $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");
189 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 minutes/, "cert expires in 2 minutes output");
190 is( $res->return_code, 2, "cert expires in 2 minutes exit code" );
191
192 $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");
193 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 hours/, "cert expires in 2 hours output");
194 is( $res->return_code, 2, "cert expires in 2 hours exit code" );
195
196 $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");
197 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expired on/, "Certificate expired output");
198 is( $res->return_code, 2, "Certificate expired exit code" );
199 };
200
201 $res = NPTest->testCmd( "./$plugin --ssl $host_tls_http -E" );
202 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' );
203 like ( $res->output, '/time_ssl=[\d\.]+/', 'Extended Performance Data SSL Output OK' );
204
205 $res = NPTest->testCmd(
206 "./$plugin --ssl -H www.e-paycobalt.com"
207 );
208 cmp_ok( $res->return_code, "==", 0, "Can read https for www.e-paycobalt.com (uses AES certificate)" );
209
210
211 $res = NPTest->testCmd( "./$plugin -H www.mozilla.com -u /firefox -f follow" );
212 is( $res->return_code, 0, "Redirection based on location is okay");
213
214 $res = NPTest->testCmd( "./$plugin -H www.mozilla.com --extended-perfdata" );
215 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' );
216}
diff --git a/plugins/t/check_http.t b/plugins/t/check_http.t
index b3760eb..e92681e 100644
--- a/plugins/t/check_http.t
+++ b/plugins/t/check_http.t
@@ -14,6 +14,8 @@ plan tests => 50;
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", "A host providing the HTTP Service (a web server)", "localhost"); 20my $host_tcp_http = getTestParameter("NP_HOST_TCP_HTTP", "A host providing the HTTP Service (a web server)", "localhost");
19my $host_tls_http = getTestParameter("NP_HOST_TLS_HTTP", "A host providing the HTTPS Service (a tls web server)", "localhost"); 21my $host_tls_http = getTestParameter("NP_HOST_TLS_HTTP", "A host providing the HTTPS Service (a tls web server)", "localhost");
@@ -29,24 +31,24 @@ my $faketime = -x '/usr/bin/faketime' ? 1 : 0;
29 31
30 32
31$res = NPTest->testCmd( 33$res = NPTest->testCmd(
32 "./check_http $host_tcp_http -wt 300 -ct 600" 34 "./$plugin $host_tcp_http -wt 300 -ct 600"
33 ); 35 );
34cmp_ok( $res->return_code, '==', 0, "Webserver $host_tcp_http responded" ); 36cmp_ok( $res->return_code, '==', 0, "Webserver $host_tcp_http responded" );
35like( $res->output, $successOutput, "Output OK" ); 37like( $res->output, $successOutput, "Output OK" );
36 38
37$res = NPTest->testCmd( 39$res = NPTest->testCmd(
38 "./check_http $host_tcp_http -wt 300 -ct 600 -v -v -v -k 'bob:there' -k 'carl:frown'" 40 "./$plugin $host_tcp_http -wt 300 -ct 600 -v -v -v -k 'bob:there' -k 'carl:frown'"
39 ); 41 );
40like( $res->output, '/bob:there\r\ncarl:frown\r\n/', "Got headers with multiple -k options" ); 42like( $res->output, '/bob:there\r\ncarl:frown\r\n/', "Got headers with multiple -k options" );
41 43
42$res = NPTest->testCmd( 44$res = NPTest->testCmd(
43 "./check_http $host_nonresponsive -wt 1 -ct 2 -t 3" 45 "./$plugin $host_nonresponsive -wt 1 -ct 2 -t 3"
44 ); 46 );
45cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" ); 47cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" );
46cmp_ok( $res->output, 'eq', "CRITICAL - Socket timeout after 3 seconds", "Output OK"); 48cmp_ok( $res->output, 'eq', "CRITICAL - Socket timeout after 3 seconds", "Output OK");
47 49
48$res = NPTest->testCmd( 50$res = NPTest->testCmd(
49 "./check_http $hostname_invalid -wt 1 -ct 2" 51 "./$plugin $hostname_invalid -wt 1 -ct 2"
50 ); 52 );
51cmp_ok( $res->return_code, '==', 2, "Webserver $hostname_invalid not valid" ); 53cmp_ok( $res->return_code, '==', 2, "Webserver $hostname_invalid not valid" );
52# The first part of the message comes from the OS catalogue, so cannot check this. 54# The first part of the message comes from the OS catalogue, so cannot check this.
@@ -55,86 +57,86 @@ cmp_ok( $res->return_code, '==', 2, "Webserver $hostname_invalid not valid" );
55like( $res->output, "/Unable to open TCP socket|Socket timeout after/", "Output OK"); 57like( $res->output, "/Unable to open TCP socket|Socket timeout after/", "Output OK");
56 58
57# host header checks 59# host header checks
58$res = NPTest->testCmd("./check_http -v -H $host_tcp_http"); 60$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http");
59like( $res->output, '/^Host: '.$host_tcp_http.'\s*$/ms', "Host Header OK" ); 61like( $res->output, '/^Host: '.$host_tcp_http.'\s*$/ms', "Host Header OK" );
60 62
61$res = NPTest->testCmd("./check_http -v -H $host_tcp_http -p 80"); 63$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http -p 80");
62like( $res->output, '/^Host: '.$host_tcp_http.'\s*$/ms', "Host Header OK" ); 64like( $res->output, '/^Host: '.$host_tcp_http.'\s*$/ms', "Host Header OK" );
63 65
64$res = NPTest->testCmd("./check_http -v -H $host_tcp_http:8080 -p 80"); 66$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http:8080 -p 80");
65like( $res->output, '/^Host: '.$host_tcp_http.':8080\s*$/ms', "Host Header OK" ); 67like( $res->output, '/^Host: '.$host_tcp_http.':8080\s*$/ms', "Host Header OK" );
66 68
67$res = NPTest->testCmd("./check_http -v -H $host_tcp_http:8080 -p 80"); 69$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http:8080 -p 80");
68like( $res->output, '/^Host: '.$host_tcp_http.':8080\s*$/ms', "Host Header OK" ); 70like( $res->output, '/^Host: '.$host_tcp_http.':8080\s*$/ms', "Host Header OK" );
69 71
70SKIP: { 72SKIP: {
71 skip "No internet access", 3 if $internet_access eq "no"; 73 skip "No internet access", 3 if $internet_access eq "no";
72 74
73 $res = NPTest->testCmd("./check_http -v -H $host_tls_http -S"); 75 $res = NPTest->testCmd("./$plugin -v -H $host_tls_http -S");
74 like( $res->output, '/^Host: '.$host_tls_http.'\s*$/ms', "Host Header OK" ); 76 like( $res->output, '/^Host: '.$host_tls_http.'\s*$/ms', "Host Header OK" );
75 77
76 $res = NPTest->testCmd("./check_http -v -H $host_tls_http:8080 -S -p 443"); 78 $res = NPTest->testCmd("./$plugin -v -H $host_tls_http:8080 -S -p 443");
77 like( $res->output, '/^Host: '.$host_tls_http.':8080\s*$/ms', "Host Header OK" ); 79 like( $res->output, '/^Host: '.$host_tls_http.':8080\s*$/ms', "Host Header OK" );
78 80
79 $res = NPTest->testCmd("./check_http -v -H $host_tls_http:443 -S -p 443"); 81 $res = NPTest->testCmd("./$plugin -v -H $host_tls_http:443 -S -p 443");
80 like( $res->output, '/^Host: '.$host_tls_http.'\s*$/ms', "Host Header OK" ); 82 like( $res->output, '/^Host: '.$host_tls_http.'\s*$/ms', "Host Header OK" );
81}; 83};
82 84
83SKIP: { 85SKIP: {
84 skip "No host serving monitoring in index file", 7 unless $host_tcp_http2; 86 skip "No host serving monitoring in index file", 7 unless $host_tcp_http2;
85 87
86 $res = NPTest->testCmd( "./check_http -H $host_tcp_http2 -r 'monitoring'" ); 88 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'monitoring'" );
87 cmp_ok( $res->return_code, "==", 0, "Got a reference to 'monitoring'"); 89 cmp_ok( $res->return_code, "==", 0, "Got a reference to 'monitoring'");
88 90
89 $res = NPTest->testCmd( "./check_http -H $host_tcp_http2 -r 'mONiTORing'" ); 91 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing'" );
90 cmp_ok( $res->return_code, "==", 2, "Not got 'mONiTORing'"); 92 cmp_ok( $res->return_code, "==", 2, "Not got 'mONiTORing'");
91 like ( $res->output, "/pattern not found/", "Error message says 'pattern not found'"); 93 like ( $res->output, "/pattern not found/", "Error message says 'pattern not found'");
92 94
93 $res = NPTest->testCmd( "./check_http -H $host_tcp_http2 -R 'mONiTORing'" ); 95 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -R 'mONiTORing'" );
94 cmp_ok( $res->return_code, "==", 0, "But case insensitive doesn't mind 'mONiTORing'"); 96 cmp_ok( $res->return_code, "==", 0, "But case insensitive doesn't mind 'mONiTORing'");
95 97
96 $res = NPTest->testCmd( "./check_http -H $host_tcp_http2 -r 'monitoring' --invert-regex" ); 98 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'monitoring' --invert-regex" );
97 cmp_ok( $res->return_code, "==", 2, "Invert results work when found"); 99 cmp_ok( $res->return_code, "==", 2, "Invert results work when found");
98 like ( $res->output, "/pattern found/", "Error message says 'pattern found'"); 100 like ( $res->output, "/pattern found/", "Error message says 'pattern found'");
99 101
100 $res = NPTest->testCmd( "./check_http -H $host_tcp_http2 -r 'mONiTORing' --invert-regex" ); 102 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing' --invert-regex" );
101 cmp_ok( $res->return_code, "==", 0, "And also when not found"); 103 cmp_ok( $res->return_code, "==", 0, "And also when not found");
102} 104}
103SKIP: { 105SKIP: {
104 skip "No internet access", 16 if $internet_access eq "no"; 106 skip "No internet access", 16 if $internet_access eq "no";
105 107
106 $res = NPTest->testCmd( 108 $res = NPTest->testCmd(
107 "./check_http --ssl $host_tls_http" 109 "./$plugin --ssl $host_tls_http"
108 ); 110 );
109 cmp_ok( $res->return_code, '==', 0, "Can read https for $host_tls_http" ); 111 cmp_ok( $res->return_code, '==', 0, "Can read https for $host_tls_http" );
110 112
111 $res = NPTest->testCmd( "./check_http -C 1 --ssl $host_tls_http" ); 113 $res = NPTest->testCmd( "./$plugin -C 1 --ssl $host_tls_http" );
112 cmp_ok( $res->return_code, '==', 0, "Checking certificate for $host_tls_http"); 114 cmp_ok( $res->return_code, '==', 0, "Checking certificate for $host_tls_http");
113 like ( $res->output, "/Certificate '$host_tls_cert' will expire on/", "Output OK" ); 115 like ( $res->output, "/Certificate '$host_tls_cert' will expire on/", "Output OK" );
114 my $saved_cert_output = $res->output; 116 my $saved_cert_output = $res->output;
115 117
116 $res = NPTest->testCmd( "./check_http -C 8000,1 --ssl $host_tls_http" ); 118 $res = NPTest->testCmd( "./$plugin -C 8000,1 --ssl $host_tls_http" );
117 cmp_ok( $res->return_code, '==', 1, "Checking certificate for $host_tls_http"); 119 cmp_ok( $res->return_code, '==', 1, "Checking certificate for $host_tls_http");
118 like ( $res->output, qr/WARNING - Certificate '$host_tls_cert' expires in \d+ day/, "Output Warning" ); 120 like ( $res->output, qr/WARNING - Certificate '$host_tls_cert' expires in \d+ day/, "Output Warning" );
119 121
120 $res = NPTest->testCmd( "./check_http $host_tls_http -C 1" ); 122 $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" );
121 is( $res->return_code, 0, "Old syntax for cert checking okay" ); 123 is( $res->return_code, 0, "Old syntax for cert checking okay" );
122 is( $res->output, $saved_cert_output, "Same output as new syntax" ); 124 is( $res->output, $saved_cert_output, "Same output as new syntax" );
123 125
124 $res = NPTest->testCmd( "./check_http -H $host_tls_http -C 1" ); 126 $res = NPTest->testCmd( "./$plugin -H $host_tls_http -C 1" );
125 is( $res->return_code, 0, "Updated syntax for cert checking okay" ); 127 is( $res->return_code, 0, "Updated syntax for cert checking okay" );
126 is( $res->output, $saved_cert_output, "Same output as new syntax" ); 128 is( $res->output, $saved_cert_output, "Same output as new syntax" );
127 129
128 $res = NPTest->testCmd( "./check_http -C 1 $host_tls_http" ); 130 $res = NPTest->testCmd( "./$plugin -C 1 $host_tls_http" );
129 cmp_ok( $res->output, 'eq', $saved_cert_output, "--ssl option automatically added"); 131 cmp_ok( $res->output, 'eq', $saved_cert_output, "--ssl option automatically added");
130 132
131 $res = NPTest->testCmd( "./check_http $host_tls_http -C 1" ); 133 $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" );
132 cmp_ok( $res->output, 'eq', $saved_cert_output, "Old syntax for cert checking still works"); 134 cmp_ok( $res->output, 'eq', $saved_cert_output, "Old syntax for cert checking still works");
133 135
134 # run some certificate checks with faketime 136 # run some certificate checks with faketime
135 SKIP: { 137 SKIP: {
136 skip "No faketime binary found", 7 if !$faketime; 138 skip "No faketime binary found", 12 if !$faketime;
137 $res = NPTest->testCmd("LC_TIME=C TZ=UTC ./check_http -C 1 $host_tls_http"); 139 $res = NPTest->testCmd("LC_TIME=C TZ=UTC ./$plugin -C 1 $host_tls_http");
138 like($res->output, qr/OK - Certificate '$host_tls_cert' will expire on/, "Catch cert output"); 140 like($res->output, qr/OK - Certificate '$host_tls_cert' will expire on/, "Catch cert output");
139 is( $res->return_code, 0, "Catch cert output exit code" ); 141 is( $res->return_code, 0, "Catch cert output exit code" );
140 my($mon,$day,$hour,$min,$sec,$year) = ($res->output =~ /(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)/); 142 my($mon,$day,$hour,$min,$sec,$year) = ($res->output =~ /(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)/);
@@ -144,51 +146,51 @@ SKIP: {
144 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}; 146 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};
145 my $ts = mktime($sec, $min, $hour, $day, $months->{$mon}, $year-1900); 147 my $ts = mktime($sec, $min, $hour, $day, $months->{$mon}, $year-1900);
146 my $time = strftime("%Y-%m-%d %H:%M:%S", localtime($ts)); 148 my $time = strftime("%Y-%m-%d %H:%M:%S", localtime($ts));
147 $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"); 149 $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");
148 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' just expired/, "Output on expire date"); 150 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' just expired/, "Output on expire date");
149 151
150 $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"); 152 $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");
151 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 0 minutes/, "cert expires in 1 second output"); 153 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 0 minutes/, "cert expires in 1 second output");
152 154
153 $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"); 155 $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");
154 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 minutes/, "cert expires in 2 minutes output"); 156 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 minutes/, "cert expires in 2 minutes output");
155 157
156 $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"); 158 $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");
157 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 hours/, "cert expires in 2 hours output"); 159 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 hours/, "cert expires in 2 hours output");
158 160
159 $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"); 161 $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");
160 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expired on/, "Certificate expired output"); 162 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expired on/, "Certificate expired output");
161 }; 163 };
162 164
163 $res = NPTest->testCmd( "./check_http --ssl $host_tls_http -E" ); 165 $res = NPTest->testCmd( "./$plugin --ssl $host_tls_http -E" );
164 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' ); 166 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' );
165 like ( $res->output, '/time_ssl=[\d\.]+/', 'Extended Performance Data SSL Output OK' ); 167 like ( $res->output, '/time_ssl=[\d\.]+/', 'Extended Performance Data SSL Output OK' );
166 168
167 $res = NPTest->testCmd( 169 $res = NPTest->testCmd(
168 "./check_http --ssl -H www.e-paycobalt.com" 170 "./$plugin --ssl -H www.e-paycobalt.com"
169 ); 171 );
170 cmp_ok( $res->return_code, "==", 0, "Can read https for www.e-paycobalt.com (uses AES certificate)" ); 172 cmp_ok( $res->return_code, "==", 0, "Can read https for www.e-paycobalt.com (uses AES certificate)" );
171 173
172 174
173 $res = NPTest->testCmd( "./check_http -H www.mozilla.com -u /firefox -f follow" ); 175 $res = NPTest->testCmd( "./$plugin -H www.mozilla.com -u /firefox -f follow" );
174 is( $res->return_code, 0, "Redirection based on location is okay"); 176 is( $res->return_code, 0, "Redirection based on location is okay");
175 177
176 $res = NPTest->testCmd( "./check_http -H www.mozilla.com --extended-perfdata" ); 178 $res = NPTest->testCmd( "./$plugin -H www.mozilla.com --extended-perfdata" );
177 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' ); 179 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' );
178} 180}
179 181
180SKIP: { 182SKIP: {
181 skip "No internet access or proxy configured", 6 if $internet_access eq "no" or ! $host_tcp_proxy; 183 skip "No internet access or proxy configured", 6 if $internet_access eq "no" or ! $host_tcp_proxy;
182 184
183 $res = NPTest->testCmd( "./check_http -I $host_tcp_proxy -p $port_tcp_proxy -u http://$host_tcp_http -e 200,301,302"); 185 $res = NPTest->testCmd( "./$plugin -I $host_tcp_proxy -p $port_tcp_proxy -u http://$host_tcp_http -e 200,301,302");
184 is( $res->return_code, 0, "Proxy HTTP works"); 186 is( $res->return_code, 0, "Proxy HTTP works");
185 like($res->output, qr/OK: Status line output matched/, "Proxy HTTP Output is sufficent"); 187 like($res->output, qr/OK: Status line output matched/, "Proxy HTTP Output is sufficent");
186 188
187 $res = NPTest->testCmd( "./check_http -I $host_tcp_proxy -p $port_tcp_proxy -H $host_tls_http -S -j CONNECT"); 189 $res = NPTest->testCmd( "./$plugin -I $host_tcp_proxy -p $port_tcp_proxy -H $host_tls_http -S -j CONNECT");
188 is( $res->return_code, 0, "Proxy HTTP CONNECT works"); 190 is( $res->return_code, 0, "Proxy HTTP CONNECT works");
189 like($res->output, qr/HTTP OK:/, "Proxy HTTP CONNECT output sufficent"); 191 like($res->output, qr/HTTP OK:/, "Proxy HTTP CONNECT output sufficent");
190 192
191 $res = NPTest->testCmd( "./check_http -I $host_tcp_proxy -p $port_tcp_proxy -H $host_tls_http -S -j CONNECT:HEAD"); 193 $res = NPTest->testCmd( "./$plugin -I $host_tcp_proxy -p $port_tcp_proxy -H $host_tls_http -S -j CONNECT:HEAD");
192 is( $res->return_code, 0, "Proxy HTTP CONNECT works with override method"); 194 is( $res->return_code, 0, "Proxy HTTP CONNECT works with override method");
193 like($res->output, qr/HTTP OK:/, "Proxy HTTP CONNECT output sufficent"); 195 like($res->output, qr/HTTP OK:/, "Proxy HTTP CONNECT output sufficent");
194} 196}
diff --git a/plugins/tests/check_curl.t b/plugins/tests/check_curl.t
new file mode 100755
index 0000000..e182623
--- /dev/null
+++ b/plugins/tests/check_curl.t
@@ -0,0 +1,497 @@
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
35# look for libcurl version to see if some advanced checks are possible (>= 7.49.0)
36my $advanced_checks = 12;
37my $use_advanced_checks = 0;
38my $required_version = '7.49.0';
39my $virtual_host = 'www.somefunnyhost.com';
40my $virtual_port = 42;
41my $curl_version = '';
42open (my $fh, '-|', "./$plugin --version") or die;
43while (<$fh>) {
44 if (m{libcurl/([\d.]+)\s}) {
45 $curl_version = $1;
46 last;
47 }
48}
49close ($fh);
50if ($curl_version) {
51 my ($major, $minor, $release) = split (/\./, $curl_version);
52 my ($req_major, $req_minor, $req_release) = split (/\./, $required_version);
53 my $check = ($major <=> $req_major or $minor <=> $req_minor or $release <=> $req_release);
54 if ($check >= 0) {
55 $use_advanced_checks = 1;
56 print "Found libcurl $major.$minor.$release. Using advanced checks\n";
57 }
58}
59
60if ($@) {
61 plan skip_all => "Missing required module for test: $@";
62} else {
63 if (-x "./$plugin") {
64 plan tests => $common_tests * 2 + $ssl_only_tests + $advanced_checks;
65 } else {
66 plan skip_all => "No $plugin compiled";
67 }
68}
69
70my $servers = { http => 0 }; # HTTP::Daemon should always be available
71eval { require HTTP::Daemon::SSL };
72if ($@) {
73 diag "Cannot load HTTP::Daemon::SSL: $@";
74} else {
75 $servers->{https} = 0;
76}
77
78# set a fixed version, so the header size doesn't vary
79$HTTP::Daemon::VERSION = "1.00";
80
81my $port_http = 50000 + int(rand(1000));
82my $port_https = $port_http + 1;
83my $port_https_expired = $port_http + 2;
84
85# This array keeps sockets around for implementing timeouts
86my @persist;
87
88# Start up all servers
89my @pids;
90my $pid = fork();
91if ($pid) {
92 # Parent
93 push @pids, $pid;
94 if (exists $servers->{https}) {
95 # Fork a normal HTTPS server
96 $pid = fork();
97 if ($pid) {
98 # Parent
99 push @pids, $pid;
100 # Fork an expired cert server
101 $pid = fork();
102 if ($pid) {
103 push @pids, $pid;
104 } else {
105 my $d = HTTP::Daemon::SSL->new(
106 LocalPort => $port_https_expired,
107 LocalAddr => "127.0.0.1",
108 SSL_cert_file => "$Bin/certs/expired-cert.pem",
109 SSL_key_file => "$Bin/certs/expired-key.pem",
110 ) || die;
111 print "Please contact https expired at: <URL:", $d->url, ">\n";
112 run_server( $d );
113 exit;
114 }
115 } else {
116 my $d = HTTP::Daemon::SSL->new(
117 LocalPort => $port_https,
118 LocalAddr => "127.0.0.1",
119 SSL_cert_file => "$Bin/certs/server-cert.pem",
120 SSL_key_file => "$Bin/certs/server-key.pem",
121 ) || die;
122 print "Please contact https at: <URL:", $d->url, ">\n";
123 run_server( $d );
124 exit;
125 }
126 }
127 # give our webservers some time to startup
128 sleep(1);
129} else {
130 # Child
131 #print "child\n";
132 my $d = HTTP::Daemon->new(
133 LocalPort => $port_http,
134 LocalAddr => "127.0.0.1",
135 ) || die;
136 print "Please contact http at: <URL:", $d->url, ">\n";
137 run_server( $d );
138 exit;
139}
140
141# Run the same server on http and https
142sub run_server {
143 my $d = shift;
144 MAINLOOP: while (my $c = $d->accept ) {
145 while (my $r = $c->get_request) {
146 if ($r->method eq "GET" and $r->url->path =~ m^/statuscode/(\d+)^) {
147 $c->send_basic_header($1);
148 $c->send_crlf;
149 } elsif ($r->method eq "GET" and $r->url->path =~ m^/file/(.*)^) {
150 $c->send_basic_header;
151 $c->send_crlf;
152 $c->send_file_response("$Bin/var/$1");
153 } elsif ($r->method eq "GET" and $r->url->path eq "/slow") {
154 $c->send_basic_header;
155 $c->send_crlf;
156 sleep 1;
157 $c->send_response("slow");
158 } elsif ($r->url->path eq "/method") {
159 if ($r->method eq "DELETE") {
160 $c->send_error(HTTP::Status->RC_METHOD_NOT_ALLOWED);
161 } elsif ($r->method eq "foo") {
162 $c->send_error(HTTP::Status->RC_NOT_IMPLEMENTED);
163 } else {
164 $c->send_status_line(200, $r->method);
165 }
166 } elsif ($r->url->path eq "/postdata") {
167 $c->send_basic_header;
168 $c->send_crlf;
169 $c->send_response($r->method.":".$r->content);
170 } elsif ($r->url->path eq "/redirect") {
171 $c->send_redirect( "/redirect2" );
172 } elsif ($r->url->path eq "/redir_external") {
173 $c->send_redirect(($d->isa('HTTP::Daemon::SSL') ? "https" : "http") . "://169.254.169.254/redirect2" );
174 } elsif ($r->url->path eq "/redirect2") {
175 $c->send_basic_header;
176 $c->send_crlf;
177 $c->send_response(HTTP::Response->new( 200, 'OK', undef, 'redirected' ));
178 } elsif ($r->url->path eq "/redir_timeout") {
179 $c->send_redirect( "/timeout" );
180 } elsif ($r->url->path eq "/timeout") {
181 # Keep $c from being destroyed, but prevent severe leaks
182 unshift @persist, $c;
183 delete($persist[1000]);
184 next MAINLOOP;
185 } elsif ($r->url->path eq "/header_check") {
186 $c->send_basic_header;
187 $c->send_header('foo');
188 $c->send_crlf;
189 } elsif ($r->url->path eq "/virtual_port") {
190 # return sent Host header
191 $c->send_basic_header;
192 $c->send_crlf;
193 $c->send_response(HTTP::Response->new( 200, 'OK', undef, $r->header ('Host')));
194 } else {
195 $c->send_error(HTTP::Status->RC_FORBIDDEN);
196 }
197 $c->close;
198 }
199 }
200}
201
202END {
203 foreach my $pid (@pids) {
204 if ($pid) { print "Killing $pid\n"; kill "INT", $pid }
205 }
206};
207
208if ($ARGV[0] && $ARGV[0] eq "-d") {
209 while (1) {
210 sleep 100;
211 }
212}
213
214my $result;
215my $command = "./$plugin -H 127.0.0.1";
216
217run_common_tests( { command => "$command -p $port_http" } );
218SKIP: {
219 skip "HTTP::Daemon::SSL not installed", $common_tests + $ssl_only_tests if ! exists $servers->{https};
220 run_common_tests( { command => "$command -p $port_https", ssl => 1 } );
221
222 $result = NPTest->testCmd( "$command -p $port_https -S -C 14" );
223 is( $result->return_code, 0, "$command -p $port_https -S -C 14" );
224 is( $result->output, 'OK - Certificate \'Ton Voon\' will expire on Sun Mar 3 21:41:28 2019 +0000.', "output ok" );
225
226 $result = NPTest->testCmd( "$command -p $port_https -S -C 14000" );
227 is( $result->return_code, 1, "$command -p $port_https -S -C 14000" );
228 like( $result->output, '/WARNING - Certificate \'Ton Voon\' expires in \d+ day\(s\) \(Sun Mar 3 21:41:28 2019 \+0000\)./', "output ok" );
229
230 # Expired cert tests
231 $result = NPTest->testCmd( "$command -p $port_https -S -C 13960,14000" );
232 is( $result->return_code, 2, "$command -p $port_https -S -C 13960,14000" );
233 like( $result->output, '/CRITICAL - Certificate \'Ton Voon\' expires in \d+ day\(s\) \(Sun Mar 3 21:41:28 2019 \+0000\)./', "output ok" );
234
235 $result = NPTest->testCmd( "$command -p $port_https_expired -S -C 7" );
236 is( $result->return_code, 2, "$command -p $port_https_expired -S -C 7" );
237 is( $result->output,
238 'CRITICAL - Certificate \'Ton Voon\' expired on Thu Mar 5 00:13:16 2009 +0000.',
239 "output ok" );
240
241}
242
243my $cmd;
244
245# advanced checks with virtual hostname and virtual port
246SKIP: {
247 skip "libcurl version is smaller than $required_version", 6 unless $use_advanced_checks;
248
249 # http without virtual port
250 $cmd = "./$plugin -H $virtual_host -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host:$port_http\$";
251 $result = NPTest->testCmd( $cmd );
252 is( $result->return_code, 0, $cmd);
253 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
254
255 # http with virtual port (!= 80)
256 $cmd = "./$plugin -H $virtual_host:$virtual_port -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host:$virtual_port\$";
257 $result = NPTest->testCmd( $cmd );
258 is( $result->return_code, 0, $cmd);
259 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
260
261 # http with virtual port (80)
262 $cmd = "./$plugin -H $virtual_host:80 -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host\$";
263 $result = NPTest->testCmd( $cmd );
264 is( $result->return_code, 0, $cmd);
265 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
266}
267
268# and the same for SSL
269SKIP: {
270 skip "libcurl version is smaller than $required_version and/or HTTP::Daemon::SSL not installed", 6 if ! exists $servers->{https} or not $use_advanced_checks;
271 # https without virtual port
272 $cmd = "./$plugin -H $virtual_host -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host:$port_https\$";
273 $result = NPTest->testCmd( $cmd );
274 is( $result->return_code, 0, $cmd);
275 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
276
277 # https with virtual port (!= 443)
278 $cmd = "./$plugin -H $virtual_host:$virtual_port -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host:$virtual_port\$";
279 $result = NPTest->testCmd( $cmd );
280 is( $result->return_code, 0, $cmd);
281 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
282
283 # https with virtual port (443)
284 $cmd = "./$plugin -H $virtual_host:443 -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host\$";
285 $result = NPTest->testCmd( $cmd );
286 is( $result->return_code, 0, $cmd);
287 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
288}
289
290
291
292sub run_common_tests {
293 my ($opts) = @_;
294 my $command = $opts->{command};
295 if ($opts->{ssl}) {
296 $command .= " --ssl";
297 }
298
299 $result = NPTest->testCmd( "$command -u /file/root" );
300 is( $result->return_code, 0, "/file/root");
301 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second/', "Output correct" );
302
303 $result = NPTest->testCmd( "$command -u /file/root -s Root" );
304 is( $result->return_code, 0, "/file/root search for string");
305 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second/', "Output correct" );
306
307 $result = NPTest->testCmd( "$command -u /file/root -s NonRoot" );
308 is( $result->return_code, 2, "Missing string check");
309 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");
310
311 $result = NPTest->testCmd( "$command -u /file/root -s NonRootWithOver30charsAndMoreFunThanAWetFish" );
312 is( $result->return_code, 2, "Missing string check");
313 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");
314
315 $result = NPTest->testCmd( "$command -u /header_check -d foo" );
316 is( $result->return_code, 0, "header_check search for string");
317 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 96 bytes in [\d\.]+ second/', "Output correct" );
318
319 $result = NPTest->testCmd( "$command -u /header_check -d bar" );
320 is( $result->return_code, 2, "Missing header string check");
321 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");
322
323 my $cmd;
324 $cmd = "$command -u /slow";
325 $result = NPTest->testCmd( $cmd );
326 is( $result->return_code, 0, "$cmd");
327 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
328 $result->output =~ /in ([\d\.]+) second/;
329 cmp_ok( $1, ">", 1, "Time is > 1 second" );
330
331 $cmd = "$command -u /statuscode/200";
332 $result = NPTest->testCmd( $cmd );
333 is( $result->return_code, 0, $cmd);
334 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
335
336 $cmd = "$command -u /statuscode/200 -e 200";
337 $result = NPTest->testCmd( $cmd );
338 is( $result->return_code, 0, $cmd);
339 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - Status line output matched "200" - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
340
341 $cmd = "$command -u /statuscode/201";
342 $result = NPTest->testCmd( $cmd );
343 is( $result->return_code, 0, $cmd);
344 like( $result->output, '/^HTTP OK: HTTP/1.1 201 Created - \d+ bytes in [\d\.]+ second /', "Output correct: ".$result->output );
345
346 $cmd = "$command -u /statuscode/201 -e 201";
347 $result = NPTest->testCmd( $cmd );
348 is( $result->return_code, 0, $cmd);
349 like( $result->output, '/^HTTP OK: HTTP/1.1 201 Created - Status line output matched "201" - \d+ bytes in [\d\.]+ second /', "Output correct: ".$result->output );
350
351 $cmd = "$command -u /statuscode/201 -e 200";
352 $result = NPTest->testCmd( $cmd );
353 is( $result->return_code, 2, $cmd);
354 like( $result->output, '/^HTTP CRITICAL - Invalid HTTP response received from host on port \d+: HTTP/1.1 201 Created/', "Output correct: ".$result->output );
355
356 $cmd = "$command -u /statuscode/200 -e 200,201,202";
357 $result = NPTest->testCmd( $cmd );
358 is( $result->return_code, 0, $cmd);
359 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 );
360
361 $cmd = "$command -u /statuscode/201 -e 200,201,202";
362 $result = NPTest->testCmd( $cmd );
363 is( $result->return_code, 0, $cmd);
364 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 );
365
366 $cmd = "$command -u /statuscode/203 -e 200,201,202";
367 $result = NPTest->testCmd( $cmd );
368 is( $result->return_code, 2, $cmd);
369 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 );
370
371 $cmd = "$command -j HEAD -u /method";
372 $result = NPTest->testCmd( $cmd );
373 is( $result->return_code, 0, $cmd);
374 like( $result->output, '/^HTTP OK: HTTP/1.1 200 HEAD - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
375
376 $cmd = "$command -j POST -u /method";
377 $result = NPTest->testCmd( $cmd );
378 is( $result->return_code, 0, $cmd);
379 like( $result->output, '/^HTTP OK: HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
380
381 $cmd = "$command -j GET -u /method";
382 $result = NPTest->testCmd( $cmd );
383 is( $result->return_code, 0, $cmd);
384 like( $result->output, '/^HTTP OK: HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
385
386 $cmd = "$command -u /method";
387 $result = NPTest->testCmd( $cmd );
388 is( $result->return_code, 0, $cmd);
389 like( $result->output, '/^HTTP OK: HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
390
391 $cmd = "$command -P foo -u /method";
392 $result = NPTest->testCmd( $cmd );
393 is( $result->return_code, 0, $cmd);
394 like( $result->output, '/^HTTP OK: HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
395
396 $cmd = "$command -j DELETE -u /method";
397 $result = NPTest->testCmd( $cmd );
398 is( $result->return_code, 1, $cmd);
399 like( $result->output, '/^HTTP WARNING: HTTP/1.1 405 Method Not Allowed/', "Output correct: ".$result->output );
400
401 $cmd = "$command -j foo -u /method";
402 $result = NPTest->testCmd( $cmd );
403 is( $result->return_code, 2, $cmd);
404 like( $result->output, '/^HTTP CRITICAL: HTTP/1.1 501 Not Implemented/', "Output correct: ".$result->output );
405
406 $cmd = "$command -P stufftoinclude -u /postdata -s POST:stufftoinclude";
407 $result = NPTest->testCmd( $cmd );
408 is( $result->return_code, 0, $cmd);
409 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
410
411 $cmd = "$command -j PUT -P stufftoinclude -u /postdata -s PUT:stufftoinclude";
412 $result = NPTest->testCmd( $cmd );
413 is( $result->return_code, 0, $cmd);
414 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
415
416 # To confirm that the free doesn't segfault
417 $cmd = "$command -P stufftoinclude -j PUT -u /postdata -s PUT:stufftoinclude";
418 $result = NPTest->testCmd( $cmd );
419 is( $result->return_code, 0, $cmd);
420 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
421
422 $cmd = "$command -u /redirect";
423 $result = NPTest->testCmd( $cmd );
424 is( $result->return_code, 0, $cmd);
425 like( $result->output, '/^HTTP OK: HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
426
427 $cmd = "$command -f follow -u /redirect";
428 $result = NPTest->testCmd( $cmd );
429 is( $result->return_code, 0, $cmd);
430 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
431
432 $cmd = "$command -u /redirect -k 'follow: me'";
433 $result = NPTest->testCmd( $cmd );
434 is( $result->return_code, 0, $cmd);
435 like( $result->output, '/^HTTP OK: HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
436
437 $cmd = "$command -f follow -u /redirect -k 'follow: me'";
438 $result = NPTest->testCmd( $cmd );
439 is( $result->return_code, 0, $cmd);
440 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
441
442 $cmd = "$command -f sticky -u /redirect -k 'follow: me'";
443 $result = NPTest->testCmd( $cmd );
444 is( $result->return_code, 0, $cmd);
445 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
446
447 $cmd = "$command -f stickyport -u /redirect -k 'follow: me'";
448 $result = NPTest->testCmd( $cmd );
449 is( $result->return_code, 0, $cmd);
450 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
451
452 # These tests may block
453 print "ALRM\n";
454
455 # stickyport - on full urlS port is set back to 80 otherwise
456 $cmd = "$command -f stickyport -u /redir_external -t 5 -s redirected";
457 eval {
458 local $SIG{ALRM} = sub { die "alarm\n" };
459 alarm(2);
460 $result = NPTest->testCmd( $cmd );
461 alarm(0); };
462 isnt( $@, "alarm\n", $cmd );
463 is( $result->return_code, 0, $cmd );
464
465 # Let's hope there won't be any web server on :80 returning "redirected"!
466 $cmd = "$command -f sticky -u /redir_external -t 5 -s redirected";
467 eval {
468 local $SIG{ALRM} = sub { die "alarm\n" };
469 alarm(2);
470 $result = NPTest->testCmd( $cmd );
471 alarm(0); };
472 isnt( $@, "alarm\n", $cmd );
473 isnt( $result->return_code, 0, $cmd );
474
475 # Test an external address - timeout
476 SKIP: {
477 skip "This doesn't seem to work all the time", 1 unless ($ENV{HTTP_EXTERNAL});
478 $cmd = "$command -f follow -u /redir_external -t 5";
479 eval {
480 $result = NPTest->testCmd( $cmd, 2 );
481 };
482 like( $@, "/timeout in command: $cmd/", $cmd );
483 }
484
485 $cmd = "$command -u /timeout -t 5";
486 eval {
487 $result = NPTest->testCmd( $cmd, 2 );
488 };
489 like( $@, "/timeout in command: $cmd/", $cmd );
490
491 $cmd = "$command -f follow -u /redir_timeout -t 2";
492 eval {
493 $result = NPTest->testCmd( $cmd, 5 );
494 };
495 is( $@, "", $cmd );
496
497}
diff --git a/plugins/tests/check_http.t b/plugins/tests/check_http.t
index 006f133..fe65325 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 "./$plugin") {
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