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