summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/Makefile.am10
-rw-r--r--plugins/check_by_ssh.c34
-rw-r--r--plugins/check_curl.c204
-rw-r--r--plugins/check_curl.d/check_curl_helpers.c489
-rw-r--r--plugins/check_curl.d/check_curl_helpers.h11
-rw-r--r--plugins/check_curl.d/config.h13
-rw-r--r--plugins/check_dbi.c4
-rw-r--r--plugins/check_disk.c10
-rw-r--r--plugins/check_http.c6
-rw-r--r--plugins/check_ide_smart.c259
-rw-r--r--plugins/check_ide_smart.d/config.h15
-rw-r--r--plugins/check_ldap.c12
-rw-r--r--plugins/check_mrtg.c8
-rw-r--r--plugins/check_mysql.c4
-rw-r--r--plugins/check_mysql_query.c4
-rw-r--r--plugins/check_nt.c789
-rw-r--r--plugins/check_nt.d/config.h53
-rw-r--r--plugins/check_ntp.c947
-rw-r--r--plugins/check_ntp_peer.c20
-rw-r--r--plugins/check_ntp_time.c21
-rw-r--r--plugins/check_pgsql.c8
-rw-r--r--plugins/check_ping.c4
-rw-r--r--plugins/check_procs.c2
-rw-r--r--plugins/check_radius.c132
-rw-r--r--plugins/check_radius.d/config.h6
-rw-r--r--plugins/check_real.c318
-rw-r--r--plugins/check_real.d/config.h23
-rw-r--r--plugins/check_smtp.c21
-rw-r--r--plugins/check_snmp.d/check_snmp_helpers.c14
-rw-r--r--plugins/check_snmp.d/check_snmp_helpers.h4
-rw-r--r--plugins/check_ssh.c16
-rw-r--r--plugins/check_tcp.c16
-rw-r--r--plugins/check_time.c2
-rw-r--r--plugins/check_users.c4
-rw-r--r--plugins/netutils.c10
-rw-r--r--plugins/netutils.h4
-rw-r--r--plugins/t/check_curl.t115
-rw-r--r--plugins/t/check_disk.t15
-rwxr-xr-xplugins/tests/check_curl.t319
-rwxr-xr-xplugins/tests/check_nt.t80
-rw-r--r--plugins/utils.c1
-rw-r--r--plugins/utils.h2
42 files changed, 1688 insertions, 2341 deletions
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 1a9399f0..a35f273e 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -33,7 +33,7 @@ MATHLIBS = @MATHLIBS@
33#AM_CFLAGS = -Wall 33#AM_CFLAGS = -Wall
34 34
35libexec_PROGRAMS = check_apt check_cluster check_disk check_dummy check_http check_load \ 35libexec_PROGRAMS = check_apt check_cluster check_disk check_dummy check_http check_load \
36 check_mrtg check_mrtgtraf check_ntp check_ntp_peer check_ping \ 36 check_mrtg check_mrtgtraf check_ntp_peer check_ping \
37 check_real check_smtp check_ssh check_tcp check_time check_ntp_time \ 37 check_real check_smtp check_ssh check_tcp check_time check_ntp_time \
38 check_ups check_users negate \ 38 check_ups check_users negate \
39 urlize @EXTRAS@ \ 39 urlize @EXTRAS@ \
@@ -44,7 +44,7 @@ check_tcp_programs = check_ftp check_imap check_nntp check_pop \
44 44
45EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_hpjd \ 45EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_hpjd \
46 check_swap check_fping check_ldap check_game check_dig \ 46 check_swap check_fping check_ldap check_game check_dig \
47 check_nagios check_by_ssh check_dns check_nt check_ide_smart \ 47 check_nagios check_by_ssh check_dns check_ide_smart \
48 check_procs check_mysql_query check_apt check_dbi check_curl \ 48 check_procs check_mysql_query check_apt check_dbi check_curl \
49 \ 49 \
50 tests/test_check_swap \ 50 tests/test_check_swap \
@@ -66,6 +66,7 @@ EXTRA_DIST = t \
66 check_hpjd.d \ 66 check_hpjd.d \
67 check_game.d \ 67 check_game.d \
68 check_radius.d \ 68 check_radius.d \
69 check_ide_smart.d \
69 check_curl.d \ 70 check_curl.d \
70 check_disk.d \ 71 check_disk.d \
71 check_time.d \ 72 check_time.d \
@@ -76,7 +77,6 @@ EXTRA_DIST = t \
76 check_tcp.d \ 77 check_tcp.d \
77 check_real.d \ 78 check_real.d \
78 check_ssh.d \ 79 check_ssh.d \
79 check_nt.d \
80 check_dns.d \ 80 check_dns.d \
81 check_mrtgtraf.d \ 81 check_mrtgtraf.d \
82 check_mysql_query.d \ 82 check_mysql_query.d \
@@ -157,8 +157,6 @@ check_mysql_query_CFLAGS = $(AM_CFLAGS) $(MYSQLCFLAGS)
157check_mysql_query_CPPFLAGS = $(AM_CPPFLAGS) $(MYSQLINCLUDE) 157check_mysql_query_CPPFLAGS = $(AM_CPPFLAGS) $(MYSQLINCLUDE)
158check_mysql_query_LDADD = $(NETLIBS) $(MYSQLLIBS) 158check_mysql_query_LDADD = $(NETLIBS) $(MYSQLLIBS)
159check_nagios_LDADD = $(BASEOBJS) 159check_nagios_LDADD = $(BASEOBJS)
160check_nt_LDADD = $(NETLIBS)
161check_ntp_LDADD = $(NETLIBS) $(MATHLIBS)
162check_ntp_peer_LDADD = $(NETLIBS) $(MATHLIBS) 160check_ntp_peer_LDADD = $(NETLIBS) $(MATHLIBS)
163check_pgsql_LDADD = $(NETLIBS) $(PGLIBS) 161check_pgsql_LDADD = $(NETLIBS) $(PGLIBS)
164check_ping_LDADD = $(NETLIBS) 162check_ping_LDADD = $(NETLIBS)
@@ -167,7 +165,7 @@ check_radius_LDADD = $(NETLIBS) $(RADIUSLIBS)
167check_real_LDADD = $(NETLIBS) 165check_real_LDADD = $(NETLIBS)
168check_snmp_SOURCES = check_snmp.c check_snmp.d/check_snmp_helpers.c 166check_snmp_SOURCES = check_snmp.c check_snmp.d/check_snmp_helpers.c
169check_snmp_LDADD = $(BASEOBJS) 167check_snmp_LDADD = $(BASEOBJS)
170check_snmp_LDFLAGS = $(AM_LDFLAGS) `net-snmp-config --libs` 168check_snmp_LDFLAGS = $(AM_LDFLAGS) -lm `net-snmp-config --libs`
171check_snmp_CFLAGS = $(AM_CFLAGS) `net-snmp-config --cflags` 169check_snmp_CFLAGS = $(AM_CFLAGS) `net-snmp-config --cflags`
172check_smtp_LDADD = $(SSLOBJS) 170check_smtp_LDADD = $(SSLOBJS)
173check_ssh_LDADD = $(NETLIBS) 171check_ssh_LDADD = $(NETLIBS)
diff --git a/plugins/check_by_ssh.c b/plugins/check_by_ssh.c
index df8907d9..7ffa0ded 100644
--- a/plugins/check_by_ssh.c
+++ b/plugins/check_by_ssh.c
@@ -98,7 +98,7 @@ int main(int argc, char **argv) {
98 if (child_result.cmd_error_code == 255 && config.unknown_timeout) { 98 if (child_result.cmd_error_code == 255 && config.unknown_timeout) {
99 mp_subcheck sc_ssh_execution = mp_subcheck_init(); 99 mp_subcheck sc_ssh_execution = mp_subcheck_init();
100 xasprintf(&sc_ssh_execution.output, "SSH connection failed: %s", 100 xasprintf(&sc_ssh_execution.output, "SSH connection failed: %s",
101 child_result.stderr.lines > 0 ? child_result.stderr.line[0] 101 child_result.err.lines > 0 ? child_result.err.line[0]
102 : "(no error output)"); 102 : "(no error output)");
103 103
104 sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_UNKNOWN); 104 sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_UNKNOWN);
@@ -107,34 +107,34 @@ int main(int argc, char **argv) {
107 } 107 }
108 108
109 if (verbose) { 109 if (verbose) {
110 for (size_t i = 0; i < child_result.stdout.lines; i++) { 110 for (size_t i = 0; i < child_result.out.lines; i++) {
111 printf("stdout: %s\n", child_result.stdout.line[i]); 111 printf("stdout: %s\n", child_result.out.line[i]);
112 } 112 }
113 for (size_t i = 0; i < child_result.stderr.lines; i++) { 113 for (size_t i = 0; i < child_result.err.lines; i++) {
114 printf("stderr: %s\n", child_result.stderr.line[i]); 114 printf("stderr: %s\n", child_result.err.line[i]);
115 } 115 }
116 } 116 }
117 117
118 size_t skip_stdout = 0; 118 size_t skip_stdout = 0;
119 if (config.skip_stdout) { /* --skip-stdout specified without argument */ 119 if (config.skip_stdout) { /* --skip-stdout specified without argument */
120 skip_stdout = child_result.stdout.lines; 120 skip_stdout = child_result.out.lines;
121 } else { 121 } else {
122 skip_stdout = config.stdout_lines_to_ignore; 122 skip_stdout = config.stdout_lines_to_ignore;
123 } 123 }
124 124
125 size_t skip_stderr = 0; 125 size_t skip_stderr = 0;
126 if (config.skip_stderr) { /* --skip-stderr specified without argument */ 126 if (config.skip_stderr) { /* --skip-stderr specified without argument */
127 skip_stderr = child_result.stderr.lines; 127 skip_stderr = child_result.err.lines;
128 } else { 128 } else {
129 skip_stderr = config.sterr_lines_to_ignore; 129 skip_stderr = config.sterr_lines_to_ignore;
130 } 130 }
131 131
132 /* Allow UNKNOWN or WARNING state for (non-skipped) output found on stderr */ 132 /* Allow UNKNOWN or WARNING state for (non-skipped) output found on stderr */
133 if (child_result.stderr.lines > skip_stderr && 133 if (child_result.err.lines > skip_stderr &&
134 (config.unknown_on_stderr || config.warn_on_stderr)) { 134 (config.unknown_on_stderr || config.warn_on_stderr)) {
135 mp_subcheck sc_stderr = mp_subcheck_init(); 135 mp_subcheck sc_stderr = mp_subcheck_init();
136 xasprintf(&sc_stderr.output, "remote command execution failed: %s", 136 xasprintf(&sc_stderr.output, "remote command execution failed: %s",
137 child_result.stderr.line[skip_stderr]); 137 child_result.err.line[skip_stderr]);
138 138
139 if (config.unknown_on_stderr) { 139 if (config.unknown_on_stderr) {
140 sc_stderr = mp_set_subcheck_state(sc_stderr, STATE_UNKNOWN); 140 sc_stderr = mp_set_subcheck_state(sc_stderr, STATE_UNKNOWN);
@@ -154,10 +154,10 @@ int main(int argc, char **argv) {
154 mp_subcheck sc_active_check = mp_subcheck_init(); 154 mp_subcheck sc_active_check = mp_subcheck_init();
155 xasprintf(&sc_active_check.output, "command stdout:"); 155 xasprintf(&sc_active_check.output, "command stdout:");
156 156
157 if (child_result.stdout.lines > skip_stdout) { 157 if (child_result.out.lines > skip_stdout) {
158 for (size_t i = skip_stdout; i < child_result.stdout.lines; i++) { 158 for (size_t i = skip_stdout; i < child_result.out.lines; i++) {
159 xasprintf(&sc_active_check.output, "%s\n%s", sc_active_check.output, 159 xasprintf(&sc_active_check.output, "%s\n%s", sc_active_check.output,
160 child_result.stdout.line[i]); 160 child_result.out.line[i]);
161 } 161 }
162 } else { 162 } else {
163 xasprintf(&sc_active_check.output, "remote command '%s' returned status %d", 163 xasprintf(&sc_active_check.output, "remote command '%s' returned status %d",
@@ -209,10 +209,10 @@ int main(int argc, char **argv) {
209 char *status_text; 209 char *status_text;
210 int cresult; 210 int cresult;
211 mp_subcheck sc_parse_passive = mp_subcheck_init(); 211 mp_subcheck sc_parse_passive = mp_subcheck_init();
212 for (size_t i = skip_stdout; i < child_result.stdout.lines; i++) { 212 for (size_t i = skip_stdout; i < child_result.out.lines; i++) {
213 status_text = child_result.stdout.line[i++]; 213 status_text = child_result.out.line[i++];
214 if (i == child_result.stdout.lines || 214 if (i == child_result.out.lines ||
215 strstr(child_result.stdout.line[i], "STATUS CODE: ") == NULL) { 215 strstr(child_result.out.line[i], "STATUS CODE: ") == NULL) {
216 216
217 sc_parse_passive = mp_set_subcheck_state(sc_parse_passive, STATE_UNKNOWN); 217 sc_parse_passive = mp_set_subcheck_state(sc_parse_passive, STATE_UNKNOWN);
218 xasprintf(&sc_parse_passive.output, "failed to parse output"); 218 xasprintf(&sc_parse_passive.output, "failed to parse output");
@@ -221,7 +221,7 @@ int main(int argc, char **argv) {
221 } 221 }
222 222
223 if (config.service[commands] && status_text && 223 if (config.service[commands] && status_text &&
224 sscanf(child_result.stdout.line[i], "STATUS CODE: %d", &cresult) == 1) { 224 sscanf(child_result.out.line[i], "STATUS CODE: %d", &cresult) == 1) {
225 fprintf(output_file, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n", (int)local_time, 225 fprintf(output_file, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n", (int)local_time,
226 config.host_shortname, config.service[commands++], cresult, status_text); 226 config.host_shortname, config.service[commands++], cresult, status_text);
227 } 227 }
diff --git a/plugins/check_curl.c b/plugins/check_curl.c
index e3e514ff..f63cdea2 100644
--- a/plugins/check_curl.c
+++ b/plugins/check_curl.c
@@ -92,16 +92,16 @@ typedef struct {
92static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); 92static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
93 93
94static mp_subcheck check_http(check_curl_config /*config*/, check_curl_working_state workingState, 94static mp_subcheck check_http(check_curl_config /*config*/, check_curl_working_state workingState,
95 int redir_depth); 95 long redir_depth);
96 96
97typedef struct { 97typedef struct {
98 int redir_depth; 98 long redir_depth;
99 check_curl_working_state working_state; 99 check_curl_working_state working_state;
100 int error_code; 100 int error_code;
101 check_curl_global_state curl_state; 101 check_curl_global_state curl_state;
102} redir_wrapper; 102} redir_wrapper;
103static redir_wrapper redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/, 103static redir_wrapper redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/,
104 int redir_depth, check_curl_working_state working_state); 104 long redir_depth, check_curl_working_state working_state);
105 105
106static void print_help(void); 106static void print_help(void);
107void print_usage(void); 107void print_usage(void);
@@ -120,6 +120,14 @@ mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_
120#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ 120#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */
121 121
122int main(int argc, char **argv) { 122int main(int argc, char **argv) {
123#ifdef __OpenBSD__
124 /* - rpath is required to read --extra-opts, CA and/or client certs
125 * - wpath is required to write --cookie-jar (possibly given up later)
126 * - inet is required for sockets
127 * - dns is required for name lookups */
128 pledge("stdio rpath wpath inet dns", NULL);
129#endif // __OpenBSD__
130
123 setlocale(LC_ALL, ""); 131 setlocale(LC_ALL, "");
124 bindtextdomain(PACKAGE, LOCALEDIR); 132 bindtextdomain(PACKAGE, LOCALEDIR);
125 textdomain(PACKAGE); 133 textdomain(PACKAGE);
@@ -135,6 +143,15 @@ int main(int argc, char **argv) {
135 143
136 const check_curl_config config = tmp_config.config; 144 const check_curl_config config = tmp_config.config;
137 145
146#ifdef __OpenBSD__
147 if (!config.curl_config.cookie_jar_file) {
148 if (verbose >= 2) {
149 printf(_("* No \"--cookie-jar\" is used, giving up \"wpath\" pledge(2)\n"));
150 }
151 pledge("stdio rpath inet dns", NULL);
152 }
153#endif // __OpenBSD__
154
138 if (config.output_format_is_set) { 155 if (config.output_format_is_set) {
139 mp_set_format(config.output_format); 156 mp_set_format(config.output_format);
140 } 157 }
@@ -198,7 +215,7 @@ CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) {
198#endif /* HAVE_SSL */ 215#endif /* HAVE_SSL */
199 216
200mp_subcheck check_http(const check_curl_config config, check_curl_working_state workingState, 217mp_subcheck check_http(const check_curl_config config, check_curl_working_state workingState,
201 int redir_depth) { 218 long redir_depth) {
202 219
203 // ======================= 220 // =======================
204 // Initialisation for curl 221 // Initialisation for curl
@@ -222,10 +239,35 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state
222 // ============== 239 // ==============
223 CURLcode res = curl_easy_perform(curl_state.curl); 240 CURLcode res = curl_easy_perform(curl_state.curl);
224 241
242 if (verbose > 1) {
243 printf("* curl_easy_perform returned: %s\n", curl_easy_strerror(res));
244 }
245
225 if (verbose >= 2 && workingState.http_post_data) { 246 if (verbose >= 2 && workingState.http_post_data) {
226 printf("**** REQUEST CONTENT ****\n%s\n", workingState.http_post_data); 247 printf("**** REQUEST CONTENT ****\n%s\n", workingState.http_post_data);
227 } 248 }
228 249
250 // curl_state is updated after curl_easy_perform, and with updated curl_state certificate checks can be done
251 // Check_http tries to check certs as early as possible, and exits with certificate check result by default. Behave similarly.
252#ifdef LIBCURL_FEATURE_SSL
253 if (workingState.use_ssl && config.check_cert) {
254 if (verbose > 1) {
255 printf("* adding a subcheck for the certificate\n");
256 }
257 mp_subcheck sc_certificate = check_curl_certificate_checks(
258 curl_state.curl, cert, config.days_till_exp_warn, config.days_till_exp_crit);
259
260 mp_add_subcheck_to_subcheck(&sc_result, sc_certificate);
261 if (!config.continue_after_check_cert) {
262 if (verbose > 1) {
263 printf("* returning after adding the subcheck for certificate, continuing after "
264 "checking the certificate is turned off\n");
265 }
266 return sc_result;
267 }
268 }
269#endif
270
229 mp_subcheck sc_curl = mp_subcheck_init(); 271 mp_subcheck sc_curl = mp_subcheck_init();
230 272
231 /* Curl errors, result in critical Nagios state */ 273 /* Curl errors, result in critical Nagios state */
@@ -266,18 +308,6 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state
266 // Evaluation 308 // Evaluation
267 // ========== 309 // ==========
268 310
269#ifdef LIBCURL_FEATURE_SSL
270 if (workingState.use_ssl && config.check_cert) {
271 mp_subcheck sc_certificate = check_curl_certificate_checks(
272 curl_state.curl, cert, config.days_till_exp_warn, config.days_till_exp_crit);
273
274 mp_add_subcheck_to_subcheck(&sc_result, sc_certificate);
275 if (!config.continue_after_check_cert) {
276 return sc_result;
277 }
278 }
279#endif
280
281 /* we got the data and we executed the request in a given time, so we can append 311 /* we got the data and we executed the request in a given time, so we can append
282 * performance data to the answer always 312 * performance data to the answer always
283 */ 313 */
@@ -441,19 +471,19 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state
441 "CURLINFO_REDIRECT_COUNT"); 471 "CURLINFO_REDIRECT_COUNT");
442 472
443 if (verbose >= 2) { 473 if (verbose >= 2) {
444 printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth); 474 printf(_("* curl LIBINFO_REDIRECT_COUNT is %ld\n"), redir_depth);
445 } 475 }
446 476
447 mp_subcheck sc_redir_depth = mp_subcheck_init(); 477 mp_subcheck sc_redir_depth = mp_subcheck_init();
448 if (redir_depth > config.max_depth) { 478 if (redir_depth > config.max_depth) {
449 xasprintf(&sc_redir_depth.output, 479 xasprintf(&sc_redir_depth.output,
450 "maximum redirection depth %d exceeded in libcurl", 480 "maximum redirection depth %ld exceeded in libcurl",
451 config.max_depth); 481 config.max_depth);
452 sc_redir_depth = mp_set_subcheck_state(sc_redir_depth, STATE_CRITICAL); 482 sc_redir_depth = mp_set_subcheck_state(sc_redir_depth, STATE_CRITICAL);
453 mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth); 483 mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth);
454 return sc_result; 484 return sc_result;
455 } 485 }
456 xasprintf(&sc_redir_depth.output, "redirection depth %d (of a maximum %d)", 486 xasprintf(&sc_redir_depth.output, "redirection depth %ld (of a maximum %ld)",
457 redir_depth, config.max_depth); 487 redir_depth, config.max_depth);
458 mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth); 488 mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth);
459 489
@@ -653,7 +683,7 @@ char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) {
653} 683}
654 684
655redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config config, 685redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config config,
656 int redir_depth, check_curl_working_state working_state) { 686 long redir_depth, check_curl_working_state working_state) {
657 curlhelp_statusline status_line; 687 curlhelp_statusline status_line;
658 struct phr_header headers[255]; 688 struct phr_header headers[255];
659 size_t msglen; 689 size_t msglen;
@@ -678,7 +708,7 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config
678 } 708 }
679 709
680 if (++redir_depth > config.max_depth) { 710 if (++redir_depth > config.max_depth) {
681 die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s\n"), 711 die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %ld exceeded - %s\n"),
682 config.max_depth, location); 712 config.max_depth, location);
683 } 713 }
684 714
@@ -761,7 +791,7 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config
761 } 791 }
762 792
763 /* compose new path */ 793 /* compose new path */
764 /* TODO: handle fragments and query part of URL */ 794 /* TODO: handle fragments of URL */
765 char *new_url = (char *)calloc(1, DEFAULT_BUFFER_SIZE); 795 char *new_url = (char *)calloc(1, DEFAULT_BUFFER_SIZE);
766 if (uri.pathHead) { 796 if (uri.pathHead) {
767 for (UriPathSegmentA *pathSegment = uri.pathHead; pathSegment; 797 for (UriPathSegmentA *pathSegment = uri.pathHead; pathSegment;
@@ -772,6 +802,29 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config
772 } 802 }
773 } 803 }
774 804
805 /* missing components have null,null in their UriTextRangeA
806 * add query parameters if they exist.
807 */
808 if (uri.query.first && uri.query.afterLast) {
809 // Ensure we have space for '?' + query_str + '\0' ahead of time, instead of calling strncat
810 // twice
811 size_t current_len = strlen(new_url);
812 size_t remaining_space = DEFAULT_BUFFER_SIZE - current_len - 1;
813
814 const char *query_str = uri_string(uri.query, buf, DEFAULT_BUFFER_SIZE);
815 size_t query_str_len = strlen(query_str);
816
817 if (remaining_space >= query_str_len + 1) {
818 strcat(new_url, "?");
819 strcat(new_url, query_str);
820 } else {
821 die(STATE_UNKNOWN,
822 _("HTTP UNKNOWN - No space to add query part of size %zu to the buffer, buffer has "
823 "remaining size %zu"),
824 query_str_len, current_len);
825 }
826 }
827
775 if (working_state.serverPort == new_port && 828 if (working_state.serverPort == new_port &&
776 !strncmp(working_state.server_address, new_host, MAX_IPV4_HOSTLENGTH) && 829 !strncmp(working_state.server_address, new_host, MAX_IPV4_HOSTLENGTH) &&
777 (working_state.host_name && 830 (working_state.host_name &&
@@ -834,7 +887,8 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) {
834 COOKIE_JAR, 887 COOKIE_JAR,
835 HAPROXY_PROTOCOL, 888 HAPROXY_PROTOCOL,
836 STATE_REGEX, 889 STATE_REGEX,
837 OUTPUT_FORMAT 890 OUTPUT_FORMAT,
891 NO_PROXY,
838 }; 892 };
839 893
840 static struct option longopts[] = { 894 static struct option longopts[] = {
@@ -849,6 +903,8 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) {
849 {"url", required_argument, 0, 'u'}, 903 {"url", required_argument, 0, 'u'},
850 {"port", required_argument, 0, 'p'}, 904 {"port", required_argument, 0, 'p'},
851 {"authorization", required_argument, 0, 'a'}, 905 {"authorization", required_argument, 0, 'a'},
906 {"proxy", required_argument, 0, 'x'},
907 {"noproxy", required_argument, 0, NO_PROXY},
852 {"proxy-authorization", required_argument, 0, 'b'}, 908 {"proxy-authorization", required_argument, 0, 'b'},
853 {"header-string", required_argument, 0, 'd'}, 909 {"header-string", required_argument, 0, 'd'},
854 {"string", required_argument, 0, 's'}, 910 {"string", required_argument, 0, 's'},
@@ -921,7 +977,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) {
921 977
922 while (true) { 978 while (true) {
923 int option_index = getopt_long( 979 int option_index = getopt_long(
924 argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:DnlLS::m:M:NEB", 980 argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:x:b:d:e:p:s:R:r:u:f:C:J:K:DnlLS::m:M:NEB",
925 longopts, &option); 981 longopts, &option);
926 if (option_index == -1 || option_index == EOF || option_index == 1) { 982 if (option_index == -1 || option_index == EOF || option_index == 1) {
927 break; 983 break;
@@ -950,7 +1006,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) {
950 case 'c': /* critical time threshold */ 1006 case 'c': /* critical time threshold */
951 { 1007 {
952 mp_range_parsed critical_range = mp_parse_range_string(optarg); 1008 mp_range_parsed critical_range = mp_parse_range_string(optarg);
953 if (critical_range.error != MP_PARSING_SUCCES) { 1009 if (critical_range.error != MP_PARSING_SUCCESS) {
954 die(STATE_UNKNOWN, "failed to parse critical threshold: %s", optarg); 1010 die(STATE_UNKNOWN, "failed to parse critical threshold: %s", optarg);
955 } 1011 }
956 result.config.thlds = mp_thresholds_set_crit(result.config.thlds, critical_range.range); 1012 result.config.thlds = mp_thresholds_set_crit(result.config.thlds, critical_range.range);
@@ -959,7 +1015,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) {
959 { 1015 {
960 mp_range_parsed warning_range = mp_parse_range_string(optarg); 1016 mp_range_parsed warning_range = mp_parse_range_string(optarg);
961 1017
962 if (warning_range.error != MP_PARSING_SUCCES) { 1018 if (warning_range.error != MP_PARSING_SUCCESS) {
963 die(STATE_UNKNOWN, "failed to parse warning threshold: %s", optarg); 1019 die(STATE_UNKNOWN, "failed to parse warning threshold: %s", optarg);
964 } 1020 }
965 result.config.thlds = mp_thresholds_set_warn(result.config.thlds, warning_range.range); 1021 result.config.thlds = mp_thresholds_set_warn(result.config.thlds, warning_range.range);
@@ -1009,6 +1065,10 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) {
1009 strncpy(result.config.curl_config.user_auth, optarg, MAX_INPUT_BUFFER - 1); 1065 strncpy(result.config.curl_config.user_auth, optarg, MAX_INPUT_BUFFER - 1);
1010 result.config.curl_config.user_auth[MAX_INPUT_BUFFER - 1] = 0; 1066 result.config.curl_config.user_auth[MAX_INPUT_BUFFER - 1] = 0;
1011 break; 1067 break;
1068 case 'x': /* proxy info */
1069 strncpy(result.config.curl_config.proxy, optarg, DEFAULT_BUFFER_SIZE - 1);
1070 result.config.curl_config.proxy[DEFAULT_BUFFER_SIZE - 1] = 0;
1071 break;
1012 case 'b': /* proxy-authorization info */ 1072 case 'b': /* proxy-authorization info */
1013 strncpy(result.config.curl_config.proxy_auth, optarg, MAX_INPUT_BUFFER - 1); 1073 strncpy(result.config.curl_config.proxy_auth, optarg, MAX_INPUT_BUFFER - 1);
1014 result.config.curl_config.proxy_auth[MAX_INPUT_BUFFER - 1] = 0; 1074 result.config.curl_config.proxy_auth[MAX_INPUT_BUFFER - 1] = 0;
@@ -1225,7 +1285,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) {
1225 result.config.curl_config.sin_family = AF_INET; 1285 result.config.curl_config.sin_family = AF_INET;
1226 break; 1286 break;
1227 case '6': 1287 case '6':
1228#if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) 1288#if defined(LIBCURL_FEATURE_IPV6)
1229 result.config.curl_config.sin_family = AF_INET6; 1289 result.config.curl_config.sin_family = AF_INET6;
1230#else 1290#else
1231 usage4(_("IPv6 support not available")); 1291 usage4(_("IPv6 support not available"));
@@ -1235,7 +1295,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) {
1235 { 1295 {
1236 mp_range_parsed foo = mp_parse_range_string(optarg); 1296 mp_range_parsed foo = mp_parse_range_string(optarg);
1237 1297
1238 if (foo.error != MP_PARSING_SUCCES) { 1298 if (foo.error != MP_PARSING_SUCCESS) {
1239 die(STATE_CRITICAL, "failed to parse page size limits: %s", optarg); 1299 die(STATE_CRITICAL, "failed to parse page size limits: %s", optarg);
1240 } 1300 }
1241 1301
@@ -1304,6 +1364,10 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) {
1304 case HAPROXY_PROTOCOL: 1364 case HAPROXY_PROTOCOL:
1305 result.config.curl_config.haproxy_protocol = true; 1365 result.config.curl_config.haproxy_protocol = true;
1306 break; 1366 break;
1367 case NO_PROXY:
1368 strncpy(result.config.curl_config.no_proxy, optarg, DEFAULT_BUFFER_SIZE - 1);
1369 result.config.curl_config.no_proxy[DEFAULT_BUFFER_SIZE - 1] = 0;
1370 break;
1307 case '?': 1371 case '?':
1308 /* print short usage statement if args not parsable */ 1372 /* print short usage statement if args not parsable */
1309 usage5(); 1373 usage5();
@@ -1331,35 +1395,35 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) {
1331 * parameters, like -S and -C combinations */ 1395 * parameters, like -S and -C combinations */
1332 result.config.curl_config.ssl_version = CURL_SSLVERSION_DEFAULT; 1396 result.config.curl_config.ssl_version = CURL_SSLVERSION_DEFAULT;
1333 if (tls_option_optarg != NULL) { 1397 if (tls_option_optarg != NULL) {
1334 char *plus_ptr = strchr(optarg, '+'); 1398 char *plus_ptr = strchr(tls_option_optarg, '+');
1335 if (plus_ptr) { 1399 if (plus_ptr) {
1336 got_plus = true; 1400 got_plus = true;
1337 *plus_ptr = '\0'; 1401 *plus_ptr = '\0';
1338 } 1402 }
1339 1403
1340 if (optarg[0] == '2') { 1404 if (tls_option_optarg[0] == '2') {
1341 result.config.curl_config.ssl_version = CURL_SSLVERSION_SSLv2; 1405 result.config.curl_config.ssl_version = CURL_SSLVERSION_SSLv2;
1342 } else if (optarg[0] == '3') { 1406 } else if (tls_option_optarg[0] == '3') {
1343 result.config.curl_config.ssl_version = CURL_SSLVERSION_SSLv3; 1407 result.config.curl_config.ssl_version = CURL_SSLVERSION_SSLv3;
1344 } else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0")) { 1408 } else if (!strcmp(tls_option_optarg, "1") || !strcmp(tls_option_optarg, "1.0")) {
1345#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) 1409#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1346 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_0; 1410 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_0;
1347#else 1411#else
1348 result.config.ssl_version = CURL_SSLVERSION_DEFAULT; 1412 result.config.ssl_version = CURL_SSLVERSION_DEFAULT;
1349#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ 1413#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1350 } else if (!strcmp(optarg, "1.1")) { 1414 } else if (!strcmp(tls_option_optarg, "1.1")) {
1351#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) 1415#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1352 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_1; 1416 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_1;
1353#else 1417#else
1354 result.config.ssl_version = CURL_SSLVERSION_DEFAULT; 1418 result.config.ssl_version = CURL_SSLVERSION_DEFAULT;
1355#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ 1419#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1356 } else if (!strcmp(optarg, "1.2")) { 1420 } else if (!strcmp(tls_option_optarg, "1.2")) {
1357#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) 1421#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1358 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_2; 1422 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_2;
1359#else 1423#else
1360 result.config.ssl_version = CURL_SSLVERSION_DEFAULT; 1424 result.config.ssl_version = CURL_SSLVERSION_DEFAULT;
1361#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ 1425#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1362 } else if (!strcmp(optarg, "1.3")) { 1426 } else if (!strcmp(tls_option_optarg, "1.3")) {
1363#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) 1427#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0)
1364 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_3; 1428 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_3;
1365#else 1429#else
@@ -1400,7 +1464,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) {
1400 } 1464 }
1401#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */ 1465#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */
1402 if (verbose >= 2) { 1466 if (verbose >= 2) {
1403 printf(_("* Set SSL/TLS version to %d\n"), result.config.curl_config.ssl_version); 1467 printf(_("* Set SSL/TLS version to %ld\n"), result.config.curl_config.ssl_version);
1404 } 1468 }
1405 if (!specify_port) { 1469 if (!specify_port) {
1406 result.config.initial_config.serverPort = HTTPS_PORT; 1470 result.config.initial_config.serverPort = HTTPS_PORT;
@@ -1573,6 +1637,18 @@ void print_help(void) {
1573 printf(" %s\n", "--state-regex=STATE"); 1637 printf(" %s\n", "--state-regex=STATE");
1574 printf(" %s\n", _("Return STATE if regex is found, OK if not. STATE can be one of " 1638 printf(" %s\n", _("Return STATE if regex is found, OK if not. STATE can be one of "
1575 "\"critical\",\"warning\"")); 1639 "\"critical\",\"warning\""));
1640 printf(" %s\n", "-x, --proxy=PROXY_SERVER");
1641 printf(" %s\n", _("Specify the proxy in form of <scheme>://<host(name)>:<port>"));
1642 printf(" %s\n", _("Available schemes are http, https, socks4, socks4a, socks5, socks5h"));
1643 printf(" %s\n", _("If port is not specified, libcurl defaults to 1080"));
1644 printf(" %s\n", _("This value will be set as CURLOPT_PROXY"));
1645 printf(" %s\n", "--noproxy=COMMA_SEPARATED_LIST");
1646 printf(" %s\n", _("Specify hostnames, addresses and subnets where proxy should not be used"));
1647 printf(" %s\n", _("Example usage: \"example.com,::1,1.1.1.1,localhost,192.168.0.0/16\""));
1648 printf(" %s\n", _("Do not use brackets when specifying IPv6 addresses"));
1649 printf(" %s\n", _("Special case when an item is '*' : matches all hosts/addresses "
1650 "and effectively disables proxy."));
1651 printf(" %s\n", _("This value will be set as CURLOPT_NOPROXY"));
1576 printf(" %s\n", "-a, --authorization=AUTH_PAIR"); 1652 printf(" %s\n", "-a, --authorization=AUTH_PAIR");
1577 printf(" %s\n", _("Username:password on sites with basic authentication")); 1653 printf(" %s\n", _("Username:password on sites with basic authentication"));
1578 printf(" %s\n", "-b, --proxy-authorization=AUTH_PAIR"); 1654 printf(" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
@@ -1648,6 +1724,8 @@ void print_help(void) {
1648 printf(" %s\n", _("certificate matches the hostname of the server, or if the certificate")); 1724 printf(" %s\n", _("certificate matches the hostname of the server, or if the certificate"));
1649 printf(" %s\n", _("has a valid chain of trust to one of the locally installed CAs.")); 1725 printf(" %s\n", _("has a valid chain of trust to one of the locally installed CAs."));
1650 printf("\n"); 1726 printf("\n");
1727 printf(" %s\n", _("To also verify certificates, please set --verify-cert."));
1728 printf("\n");
1651 printf("%s\n", _("Examples:")); 1729 printf("%s\n", _("Examples:"));
1652 printf(" %s\n\n", "CHECK CONTENT: check_curl -w 5 -c 10 --ssl -H www.verisign.com"); 1730 printf(" %s\n\n", "CHECK CONTENT: check_curl -w 5 -c 10 --ssl -H www.verisign.com");
1653 printf(" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,")); 1731 printf(" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
@@ -1657,16 +1735,18 @@ void print_help(void) {
1657 _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); 1735 _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1658 printf(" %s\n", _("a STATE_CRITICAL will be returned.")); 1736 printf(" %s\n", _("a STATE_CRITICAL will be returned."));
1659 printf("\n"); 1737 printf("\n");
1660 printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 14"); 1738 printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 14 -D");
1661 printf(" %s\n", 1739 printf(" %s\n",
1662 _("When the certificate of 'www.verisign.com' is valid for more than 14 days,")); 1740 _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1663 printf(" %s\n", 1741 printf(" %s\n",
1664 _("a STATE_OK is returned. When the certificate is still valid, but for less than")); 1742 _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1665 printf(" %s\n", 1743 printf(" %s\n",
1666 _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when")); 1744 _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
1667 printf(" %s\n\n", _("the certificate is expired.")); 1745 printf(" %s\n", _("the certificate is expired."));
1746 printf("\n");
1747 printf(" %s\n", _("The -D flag enforces a certificate validation beyond expiration time."));
1668 printf("\n"); 1748 printf("\n");
1669 printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 30,14"); 1749 printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 30,14 -D");
1670 printf(" %s\n", 1750 printf(" %s\n",
1671 _("When the certificate of 'www.verisign.com' is valid for more than 30 days,")); 1751 _("When the certificate of 'www.verisign.com' is valid for more than 30 days,"));
1672 printf(" %s\n", 1752 printf(" %s\n",
@@ -1677,10 +1757,39 @@ void print_help(void) {
1677#endif 1757#endif
1678 1758
1679 printf("\n %s\n", "CHECK WEBSERVER CONTENT VIA PROXY:"); 1759 printf("\n %s\n", "CHECK WEBSERVER CONTENT VIA PROXY:");
1680 printf(" %s\n", _("It is recommended to use an environment proxy like:")); 1760 printf(" %s\n", _("Proxies are specified or disabled for certain hosts/addresses using environment variables"
1681 printf(" %s\n", 1761 " or -x/--proxy and --noproxy arguments:"));
1682 _("http_proxy=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org")); 1762 printf(" %s\n", _("Checked environment variables: all_proxy, http_proxy, https_proxy, no_proxy"));
1683 printf(" %s\n", _("legacy proxy requests in check_http style still work:")); 1763 printf(" %s\n", _("Environment variables can also be given in uppercase, but the lowercase ones will "
1764 "take predence if both are defined."));
1765 printf(" %s\n", _("The environment variables are overwritten by -x/--proxy and --noproxy arguments:"));
1766 printf(" %s\n", _("all_proxy/ALL_PROXY environment variables are read first, but protocol "
1767 "specific environment variables override them."));
1768 printf(" %s\n", _("If SSL is enabled and used, https_proxy/HTTPS_PROXY will be checked and overwrite "
1769 "http_proxy/HTTPS_PROXY."));
1770 printf(" %s\n", _("Curl accepts proxies using http, https, socks4, socks4a, socks5 and socks5h schemes."));
1771 printf(" %s\n", _("http_proxy=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org"));
1772 printf(" %s\n", _("http_proxy=http://used.proxy.com HTTP_PROXY=http://ignored.proxy.com ./check_curl -H www.monitoring-plugins.org"));
1773 printf(" %s\n", _(" Lowercase http_proxy takes predence over uppercase HTTP_PROXY"));
1774 printf(" %s\n", _("./check_curl -H www.monitoring-plugins.org -x http://192.168.100.35:3128"));
1775 printf(" %s\n", _("http_proxy=http://unused.proxy1.com HTTP_PROXY=http://unused.proxy2.com ./check_curl "
1776 "-H www.monitoring-plugins.org --proxy http://used.proxy"));
1777 printf(" %s\n", _(" Proxy specified by --proxy overrides any proxy specified by environment variable."));
1778 printf(" %s\n", _(" Curl uses port 1080 by default as port is not specified"));
1779 printf(" %s\n", _("HTTPS_PROXY=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org --ssl"));
1780 printf(" %s\n", _(" HTTPS_PROXY is read as --ssl is toggled"));
1781 printf(" %s\n", _("./check_curl -H www.monitoring-plugins.org --proxy socks5h://192.168.122.21"));
1782 printf(" %s\n", _("./check_curl -H www.monitoring-plugins.org -x http://unused.proxy.com --noproxy '*'"));
1783 printf(" %s\n", _(" Disabled proxy for all hosts by using '*' in no_proxy ."));
1784 printf(" %s\n", _("NO_PROXY=www.monitoring-plugins.org ./check_curl -H www.monitoring-plugins.org -x http://unused.proxy.com"));
1785 printf(" %s\n", _(" Exact matches with the hostname/address work."));
1786 printf(" %s\n", _("no_proxy=192.168.178.0/24 ./check_curl -I 192.168.178.10 -x http://proxy.acme.org"));
1787 printf(" %s\n", _("no_proxy=acme.org ./check_curl -H nonpublic.internalwebapp.acme.org -x http://proxy.acme.org"));
1788 printf(" %s\n", _(" Do not use proxy when accessing internal domains/addresses, but use a default proxy when accessing public web."));
1789 printf(" %s\n", _(" IMPORTANT: Check_curl can not always determine whether itself or the proxy will "
1790 "resolve a hostname before sending a request and getting an answer."
1791 "This can lead to DNS resolvation issues if hostname is only resolvable over proxy."));
1792 printf(" %s\n", _("Legacy proxy requests in check_http style still work:"));
1684 printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u http://www.monitoring-plugins.org/ " 1793 printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u http://www.monitoring-plugins.org/ "
1685 "-H www.monitoring-plugins.org")); 1794 "-H www.monitoring-plugins.org"));
1686 1795
@@ -1689,7 +1798,8 @@ void print_help(void) {
1689 printf(" %s\n", _("It is recommended to use an environment proxy like:")); 1798 printf(" %s\n", _("It is recommended to use an environment proxy like:"));
1690 printf(" %s\n", 1799 printf(" %s\n",
1691 _("https_proxy=http://192.168.100.35:3128 ./check_curl -H www.verisign.com -S")); 1800 _("https_proxy=http://192.168.100.35:3128 ./check_curl -H www.verisign.com -S"));
1692 printf(" %s\n", _("legacy proxy requests in check_http style might still work, but are frowned upon, so DONT:")); 1801 printf(" %s\n", _("legacy proxy requests in check_http style might still work, but are frowned "
1802 "upon, so DONT:"));
1693 printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u https://www.verisign.com/ -S -j " 1803 printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u https://www.verisign.com/ -S -j "
1694 "CONNECT -H www.verisign.com ")); 1804 "CONNECT -H www.verisign.com "));
1695 printf(" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> " 1805 printf(" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> "
@@ -1710,13 +1820,15 @@ void print_usage(void) {
1710 printf(" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n", progname); 1820 printf(" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n", progname);
1711 printf(" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate " 1821 printf(" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate "
1712 "file>] [-D]\n"); 1822 "file>] [-D]\n");
1713 printf(" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n"); 1823 printf(" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-x <proxy>]\n");
1714 printf(" [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport|curl>]\n"); 1824 printf(" [-a auth] [-b proxy_auth] [-f "
1825 "<ok|warning|critical|follow|sticky|stickyport|curl>]\n");
1715 printf(" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive " 1826 printf(" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive "
1716 "regex>]\n"); 1827 "regex>]\n");
1717 printf(" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n"); 1828 printf(" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
1718 printf(" [-A string] [-k string] [-S <version>] [--sni] [--haproxy-protocol]\n"); 1829 printf(" [-A string] [-k string] [-S <version>] [--sni] [--haproxy-protocol]\n");
1719 printf(" [-T <content-type>] [-j method]\n"); 1830 printf(" [-T <content-type>] [-j method]\n");
1831 printf(" [--noproxy=<comma separated list of hosts, IP addresses, IP CIDR subnets>\n");
1720 printf(" [--http-version=<version>] [--enable-automatic-decompression]\n"); 1832 printf(" [--http-version=<version>] [--enable-automatic-decompression]\n");
1721 printf(" [--cookie-jar=<cookie jar file>\n"); 1833 printf(" [--cookie-jar=<cookie jar file>\n");
1722 printf(" %s -H <vhost> | -I <IP-address> -C <warn_age>[,<crit_age>]\n", progname); 1834 printf(" %s -H <vhost> | -I <IP-address> -C <warn_age>[,<crit_age>]\n", progname);
diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c
index e4e7bef6..4372dc0b 100644
--- a/plugins/check_curl.d/check_curl_helpers.c
+++ b/plugins/check_curl.d/check_curl_helpers.c
@@ -3,8 +3,11 @@
3#include <arpa/inet.h> 3#include <arpa/inet.h>
4#include <netinet/in.h> 4#include <netinet/in.h>
5#include <netdb.h> 5#include <netdb.h>
6#include <stdint.h>
7#include <stdio.h>
6#include <stdlib.h> 8#include <stdlib.h>
7#include <string.h> 9#include <string.h>
10#include <sys/socket.h>
8#include "../utils.h" 11#include "../utils.h"
9#include "check_curl.d/config.h" 12#include "check_curl.d/config.h"
10#include "output.h" 13#include "output.h"
@@ -19,7 +22,7 @@ bool add_sslctx_verify_fun = false;
19check_curl_configure_curl_wrapper 22check_curl_configure_curl_wrapper
20check_curl_configure_curl(const check_curl_static_curl_config config, 23check_curl_configure_curl(const check_curl_static_curl_config config,
21 check_curl_working_state working_state, bool check_cert, 24 check_curl_working_state working_state, bool check_cert,
22 bool on_redirect_dependent, int follow_method, int max_depth) { 25 bool on_redirect_dependent, int follow_method, long max_depth) {
23 check_curl_configure_curl_wrapper result = { 26 check_curl_configure_curl_wrapper result = {
24 .errorcode = OK, 27 .errorcode = OK,
25 .curl_state = 28 .curl_state =
@@ -57,7 +60,7 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
57 result.curl_state.curl_easy_initialized = true; 60 result.curl_state.curl_easy_initialized = true;
58 61
59 if (verbose >= 1) { 62 if (verbose >= 1) {
60 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_VERBOSE, 1), 63 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_VERBOSE, 1L),
61 "CURLOPT_VERBOSE"); 64 "CURLOPT_VERBOSE");
62 } 65 }
63 66
@@ -116,6 +119,107 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
116 curl_easy_setopt(result.curl_state.curl, CURLOPT_TIMEOUT, config.socket_timeout), 119 curl_easy_setopt(result.curl_state.curl, CURLOPT_TIMEOUT, config.socket_timeout),
117 "CURLOPT_TIMEOUT"); 120 "CURLOPT_TIMEOUT");
118 121
122 /* set proxy */
123 /* http(s) proxy can either be given from the command line, or taken from environment variables */
124 /* socks4(a) / socks5(h) proxy should be given using the command line */
125
126 /* first source to check is the environment variables */
127 /* lower case proxy environment variables are almost always accepted, while some programs also checking
128 uppercase ones. discover both, but take the lowercase one if both are present */
129
130 /* extra information: libcurl does not discover the uppercase version HTTP_PROXY due to security reasons */
131 /* https://github.com/curl/curl/blob/d445f2d930ae701039518d695481ee53b8490521/lib/url.c#L1987 */
132
133 /* first environment variable to read is all_proxy. it can be overridden by protocol specific environment variables */
134 char *all_proxy_env, *all_proxy_uppercase_env;
135 all_proxy_env = getenv("all_proxy");
136 all_proxy_uppercase_env = getenv("ALL_PROXY");
137 if (all_proxy_env != NULL && strlen(all_proxy_env)){
138 working_state.curlopt_proxy = strdup(all_proxy_env);
139 if (all_proxy_uppercase_env != NULL && verbose >= 1) {
140 printf("* cURL ignoring environment variable 'ALL_PROXY' as 'all_proxy' is set\n");
141 }
142 } else if (all_proxy_uppercase_env != NULL && strlen(all_proxy_uppercase_env) > 0) {
143 working_state.curlopt_proxy = strdup(all_proxy_uppercase_env);
144 }
145
146 /* second environment variable to read is http_proxy. only set curlopt_proxy if ssl is not toggled */
147 char *http_proxy_env, *http_proxy_uppercase_env;
148 http_proxy_env = getenv("http_proxy");
149 http_proxy_uppercase_env = getenv("HTTP_PROXY");
150 if (!working_state.use_ssl){
151 if (http_proxy_env != NULL && strlen(http_proxy_env) > 0) {
152 working_state.curlopt_proxy = strdup(http_proxy_env);
153 if (http_proxy_uppercase_env != NULL && verbose >= 1) {
154 printf("* cURL ignoring environment variable 'HTTP_PROXY' as 'http_proxy' is set\n");
155 }
156 } else if (http_proxy_uppercase_env != NULL && strlen(http_proxy_uppercase_env) > 0) {
157 working_state.curlopt_proxy = strdup(http_proxy_uppercase_env);
158 }
159 }
160#ifdef LIBCURL_FEATURE_SSL
161 /* optionally read https_proxy environment variable and set curlopt_proxy if ssl is toggled */
162 char *https_proxy_env, *https_proxy_uppercase_env;
163 https_proxy_env = getenv("https_proxy");
164 https_proxy_uppercase_env = getenv("HTTPS_PROXY");
165 if (working_state.use_ssl) {
166 if (https_proxy_env != NULL && strlen(https_proxy_env) > 0) {
167 working_state.curlopt_proxy = strdup(https_proxy_env);
168 if (https_proxy_uppercase_env != NULL && verbose >= 1) {
169 printf("* cURL ignoring environment variable 'HTTPS_PROXY' as 'https_proxy' is set\n");
170 }
171 }
172 else if (https_proxy_uppercase_env != NULL && strlen(https_proxy_uppercase_env) >= 0) {
173 working_state.curlopt_proxy = strdup(https_proxy_uppercase_env);
174 }
175 }
176#endif /* LIBCURL_FEATURE_SSL */
177
178 /* second source to check for proxies is command line argument, overwriting the environment variables */
179 if (strlen(config.proxy) > 0) {
180 working_state.curlopt_proxy = strdup(config.proxy);
181 }
182
183 if (working_state.curlopt_proxy != NULL && strlen(working_state.curlopt_proxy)){
184 handle_curl_option_return_code(
185 curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXY, working_state.curlopt_proxy), "CURLOPT_PROXY");
186 if (verbose >= 1) {
187 printf("* curl CURLOPT_PROXY: %s\n", working_state.curlopt_proxy);
188 }
189 }
190
191 /* set no_proxy */
192 /* first source to check is environment variables */
193 char *no_proxy_env, *no_proxy_uppercase_env;
194 no_proxy_env = getenv("no_proxy");
195 no_proxy_uppercase_env = getenv("NO_PROXY");
196 if (no_proxy_env != NULL && strlen(no_proxy_env)){
197 working_state.curlopt_noproxy = strdup(no_proxy_env);
198 if (no_proxy_uppercase_env != NULL && verbose >= 1){
199 printf("* cURL ignoring environment variable 'NO_PROXY' as 'no_proxy' is set\n");
200 }
201 }else if (no_proxy_uppercase_env != NULL && strlen(no_proxy_uppercase_env) > 0){
202 working_state.curlopt_noproxy = strdup(no_proxy_uppercase_env);
203 }
204
205 /* second source to check for no_proxy is command line argument, overwriting the environment variables */
206 if (strlen(config.no_proxy) > 0) {
207 working_state.curlopt_noproxy = strdup(config.no_proxy);
208 }
209
210 if ( working_state.curlopt_noproxy != NULL && strlen(working_state.curlopt_noproxy)){
211 handle_curl_option_return_code(
212 curl_easy_setopt(result.curl_state.curl, CURLOPT_NOPROXY, working_state.curlopt_noproxy), "CURLOPT_NOPROXY");
213 if (verbose >= 1) {
214 printf("* curl CURLOPT_NOPROXY: %s\n", working_state.curlopt_noproxy);
215 }
216 }
217
218 int proxy_resolves_hostname = determine_hostname_resolver(working_state, config);
219 if (verbose >= 1) {
220 printf("* proxy_resolves_hostname: %d\n", proxy_resolves_hostname);
221 }
222
119 /* enable haproxy protocol */ 223 /* enable haproxy protocol */
120 if (config.haproxy_protocol) { 224 if (config.haproxy_protocol) {
121 handle_curl_option_return_code( 225 handle_curl_option_return_code(
@@ -123,11 +227,11 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
123 "CURLOPT_HAPROXYPROTOCOL"); 227 "CURLOPT_HAPROXYPROTOCOL");
124 } 228 }
125 229
126 // fill dns resolve cache to make curl connect to the given server_address instead of the 230 /* fill dns resolve cache to make curl connect to the given server_address instead of the */
127 // host_name, only required for ssl, because we use the host_name later on to make SNI happy 231 /* host_name, only required for ssl, because we use the host_name later on to make SNI happy */
128 char dnscache[DEFAULT_BUFFER_SIZE]; 232 char dnscache[DEFAULT_BUFFER_SIZE];
129 char addrstr[DEFAULT_BUFFER_SIZE / 2]; 233 char addrstr[DEFAULT_BUFFER_SIZE / 2];
130 if (working_state.use_ssl && working_state.host_name != NULL) { 234 if (working_state.use_ssl && working_state.host_name != NULL && !proxy_resolves_hostname ) {
131 char *tmp_mod_address; 235 char *tmp_mod_address;
132 236
133 /* lookup_host() requires an IPv6 address without the brackets. */ 237 /* lookup_host() requires an IPv6 address without the brackets. */
@@ -214,10 +318,10 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
214 if (working_state.http_method) { 318 if (working_state.http_method) {
215 if (!strcmp(working_state.http_method, "POST")) { 319 if (!strcmp(working_state.http_method, "POST")) {
216 handle_curl_option_return_code( 320 handle_curl_option_return_code(
217 curl_easy_setopt(result.curl_state.curl, CURLOPT_POST, 1), "CURLOPT_POST"); 321 curl_easy_setopt(result.curl_state.curl, CURLOPT_POST, 1L), "CURLOPT_POST");
218 } else if (!strcmp(working_state.http_method, "PUT")) { 322 } else if (!strcmp(working_state.http_method, "PUT")) {
219 handle_curl_option_return_code( 323 handle_curl_option_return_code(
220 curl_easy_setopt(result.curl_state.curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD"); 324 curl_easy_setopt(result.curl_state.curl, CURLOPT_UPLOAD, 1L), "CURLOPT_UPLOAD");
221 } else { 325 } else {
222 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, 326 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl,
223 CURLOPT_CUSTOMREQUEST, 327 CURLOPT_CUSTOMREQUEST,
@@ -300,10 +404,10 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
300 /* per default if we have a CA verify both the peer and the 404 /* per default if we have a CA verify both the peer and the
301 * hostname in the certificate, can be switched off later */ 405 * hostname in the certificate, can be switched off later */
302 handle_curl_option_return_code( 406 handle_curl_option_return_code(
303 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 1), 407 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 1L),
304 "CURLOPT_SSL_VERIFYPEER"); 408 "CURLOPT_SSL_VERIFYPEER");
305 handle_curl_option_return_code( 409 handle_curl_option_return_code(
306 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 2), 410 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 2L),
307 "CURLOPT_SSL_VERIFYHOST"); 411 "CURLOPT_SSL_VERIFYHOST");
308 } else { 412 } else {
309 /* backward-compatible behaviour, be tolerant in checks 413 /* backward-compatible behaviour, be tolerant in checks
@@ -311,10 +415,10 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
311 * to be less tolerant about ssl verfications 415 * to be less tolerant about ssl verfications
312 */ 416 */
313 handle_curl_option_return_code( 417 handle_curl_option_return_code(
314 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 0), 418 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 0L),
315 "CURLOPT_SSL_VERIFYPEER"); 419 "CURLOPT_SSL_VERIFYPEER");
316 handle_curl_option_return_code( 420 handle_curl_option_return_code(
317 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 0), 421 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 0L),
318 "CURLOPT_SSL_VERIFYHOST"); 422 "CURLOPT_SSL_VERIFYHOST");
319 } 423 }
320 424
@@ -438,7 +542,7 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
438 if (on_redirect_dependent) { 542 if (on_redirect_dependent) {
439 if (follow_method == FOLLOW_LIBCURL) { 543 if (follow_method == FOLLOW_LIBCURL) {
440 handle_curl_option_return_code( 544 handle_curl_option_return_code(
441 curl_easy_setopt(result.curl_state.curl, CURLOPT_FOLLOWLOCATION, 1), 545 curl_easy_setopt(result.curl_state.curl, CURLOPT_FOLLOWLOCATION, 1L),
442 "CURLOPT_FOLLOWLOCATION"); 546 "CURLOPT_FOLLOWLOCATION");
443 547
444 /* default -1 is infinite, not good, could lead to zombie plugins! 548 /* default -1 is infinite, not good, could lead to zombie plugins!
@@ -474,7 +578,7 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
474 } 578 }
475 /* no-body */ 579 /* no-body */
476 if (working_state.no_body) { 580 if (working_state.no_body) {
477 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_NOBODY, 1), 581 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_NOBODY, 1L),
478 "CURLOPT_NOBODY"); 582 "CURLOPT_NOBODY");
479 } 583 }
480 584
@@ -488,7 +592,7 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
488 curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), 592 curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4),
489 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)"); 593 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)");
490 } 594 }
491#if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) 595#if defined(LIBCURL_FEATURE_IPV6)
492 else if (config.sin_family == AF_INET6) { 596 else if (config.sin_family == AF_INET6) {
493 handle_curl_option_return_code( 597 handle_curl_option_return_code(
494 curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), 598 curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6),
@@ -562,7 +666,7 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
562 666
563void handle_curl_option_return_code(CURLcode res, const char *option) { 667void handle_curl_option_return_code(CURLcode res, const char *option) {
564 if (res != CURLE_OK) { 668 if (res != CURLE_OK) {
565 die(STATE_CRITICAL, _("Error while setting cURL option '%s': cURL returned %d - %s"), 669 die(STATE_CRITICAL, _("Error while setting cURL option '%s': cURL returned %d - %s\n"),
566 option, res, curl_easy_strerror(res)); 670 option, res, curl_easy_strerror(res));
567 } 671 }
568} 672}
@@ -589,6 +693,8 @@ check_curl_working_state check_curl_working_state_init() {
589 .serverPort = HTTP_PORT, 693 .serverPort = HTTP_PORT,
590 .use_ssl = false, 694 .use_ssl = false,
591 .no_body = false, 695 .no_body = false,
696 .curlopt_proxy = NULL,
697 .curlopt_noproxy = NULL,
592 }; 698 };
593 return result; 699 return result;
594} 700}
@@ -612,6 +718,8 @@ check_curl_config check_curl_config_init() {
612 .ca_cert = NULL, 718 .ca_cert = NULL,
613 .verify_peer_and_host = false, 719 .verify_peer_and_host = false,
614 .user_agent = {'\0'}, 720 .user_agent = {'\0'},
721 .proxy = "",
722 .no_proxy = "",
615 .proxy_auth = "", 723 .proxy_auth = "",
616 .user_auth = "", 724 .user_auth = "",
617 .http_content_type = NULL, 725 .http_content_type = NULL,
@@ -796,15 +904,17 @@ mp_subcheck check_document_dates(const curlhelp_write_curlbuf *header_buf, const
796 ((float)last_modified) / (60 * 60 * 24)); 904 ((float)last_modified) / (60 * 60 * 24));
797 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); 905 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
798 } else { 906 } else {
799 xasprintf(&sc_document_dates.output, _("Last modified %ld:%02ld:%02ld ago"), 907 xasprintf(&sc_document_dates.output, _("Last modified %lld:%02d:%02d ago"),
800 last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60); 908 (long long)last_modified / (60 * 60), (int)(last_modified / 60) % 60,
909 (int)last_modified % 60);
801 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); 910 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
802 } 911 }
803 } else { 912 } else {
804 // TODO is this the OK case? 913 // TODO is this the OK case?
805 time_t last_modified = (srv_data - doc_data); 914 time_t last_modified = (srv_data - doc_data);
806 xasprintf(&sc_document_dates.output, _("Last modified %ld:%02ld:%02ld ago"), 915 xasprintf(&sc_document_dates.output, _("Last modified %lld:%02d:%02d ago"),
807 last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60); 916 (long long)last_modified / (60 * 60), (int)(last_modified / 60) % 60,
917 (int)last_modified % 60);
808 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_OK); 918 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_OK);
809 } 919 }
810 } 920 }
@@ -1212,7 +1322,7 @@ mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_
1212 1322
1213 cert_ptr_union cert_ptr = {0}; 1323 cert_ptr_union cert_ptr = {0};
1214 cert_ptr.to_info = NULL; 1324 cert_ptr.to_info = NULL;
1215 CURLcode res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &cert_ptr.to_info); 1325 CURLcode res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &cert_ptr.to_certinfo);
1216 if (!res && cert_ptr.to_info) { 1326 if (!res && cert_ptr.to_info) {
1217# ifdef USE_OPENSSL 1327# ifdef USE_OPENSSL
1218 /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert 1328 /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert
@@ -1293,3 +1403,342 @@ char *fmt_url(check_curl_working_state workingState) {
1293 1403
1294 return url; 1404 return url;
1295} 1405}
1406
1407int determine_hostname_resolver(const check_curl_working_state working_state, const check_curl_static_curl_config config){
1408 char *host_name_display = "NULL";
1409 unsigned long host_name_len = 0;
1410 if( working_state.host_name){
1411 host_name_len = strlen(working_state.host_name);
1412 host_name_display = working_state.host_name;
1413 }
1414
1415 /* IPv4 or IPv6 version of the address */
1416 char *server_address_clean = strdup(working_state.server_address);
1417 /* server address might be a full length ipv6 address encapsulated in square brackets */
1418 if ((strnlen(working_state.server_address, MAX_IPV4_HOSTLENGTH) > 2) && (working_state.server_address[0] == '[') && (working_state.server_address[strlen(working_state.server_address)-1] == ']') ) {
1419 server_address_clean = strndup( working_state.server_address + 1, strlen(working_state.server_address) - 2);
1420 }
1421
1422 /* check curlopt_noproxy option first */
1423 /* https://curl.se/libcurl/c/CURLOPT_NOPROXY.html */
1424
1425 /* curlopt_noproxy is specified as a comma separated list of
1426 direct IPv4 or IPv6 addresses e.g 130.133.8.40, 2001:4860:4802:32::a ,
1427 IPv4 or IPv6 CIDR regions e.g 10.241.0.0/16 , abcd:ef01:2345::/48 ,
1428 direct hostnames e.g example.com, google.de */
1429
1430 if (working_state.curlopt_noproxy != NULL){
1431 char* curlopt_noproxy_copy = strdup( working_state.curlopt_noproxy);
1432 char* noproxy_item = strtok(curlopt_noproxy_copy, ",");
1433 while(noproxy_item != NULL){
1434 unsigned long noproxy_item_len = strlen(noproxy_item);
1435
1436 /* According to the CURLOPT_NOPROXY documentation: */
1437 /* https://curl.se/libcurl/c/CURLOPT_NOPROXY.html */
1438 /* The only wildcard available is a single * character, which matches all hosts, and effectively disables the proxy. */
1439 if ( strlen(noproxy_item) == 1 && noproxy_item[0] == '*'){
1440 if (verbose >= 1){
1441 printf("* noproxy includes '*' which disables proxy for all host name incl. : %s / server address incl. : %s\n", host_name_display , server_address_clean);
1442 }
1443 free(curlopt_noproxy_copy);
1444 free(server_address_clean);
1445 return 0;
1446 }
1447
1448 /* direct comparison with the server_address */
1449 if( server_address_clean != NULL && strlen(server_address_clean) == strlen(noproxy_item) && strcmp(server_address_clean, noproxy_item) == 0){
1450 if (verbose >= 1){
1451 printf("* server_address is in the no_proxy list: %s\n", noproxy_item);
1452 }
1453 free(curlopt_noproxy_copy);
1454 free(server_address_clean);
1455 return 0;
1456 }
1457
1458 /* direct comparison with the host_name */
1459 if( working_state.host_name != NULL && host_name_len == noproxy_item_len && strcmp(working_state.host_name, noproxy_item) == 0){
1460 if (verbose >= 1){
1461 printf("* host_name is in the no_proxy list: %s\n", noproxy_item);
1462 }
1463 free(curlopt_noproxy_copy);
1464 free(server_address_clean);
1465 return 0;
1466 }
1467
1468 /* check if hostname is a subdomain of the item, e.g www.example.com when token is example.com */
1469 /* subdomain1.acme.com will not will use a proxy if you only specify 'acme' in the noproxy */
1470 /* check if noproxy_item is a suffix */
1471 /* check if the character just before the suffix is '.' */
1472 if( working_state.host_name != NULL && host_name_len > noproxy_item_len){
1473 unsigned long suffix_start_idx = host_name_len - noproxy_item_len;
1474 if (strcmp(working_state.host_name + suffix_start_idx, noproxy_item ) == 0 && working_state.host_name[suffix_start_idx-1] == '.' ){
1475 if (verbose >= 1){
1476 printf("* host_name: %s is a subdomain of the no_proxy list item: %s\n", working_state.host_name , noproxy_item);
1477 }
1478 free(curlopt_noproxy_copy);
1479 free(server_address_clean);
1480 return 0;
1481 }
1482 }
1483
1484 // noproxy_item could be a CIDR IP range
1485 if( server_address_clean != NULL && strlen(server_address_clean)){
1486
1487 int ip_addr_inside_cidr_ret = ip_addr_inside_cidr(noproxy_item, server_address_clean);
1488
1489 switch(ip_addr_inside_cidr_ret){
1490 case 1:
1491 return 0;
1492 break;
1493 case 0:
1494 if(verbose >= 1){
1495 printf("server address: %s is not inside IP cidr: %s\n", server_address_clean, noproxy_item);
1496 }
1497 break;
1498 case -1:
1499 if(verbose >= 1){
1500 printf("could not fully determine if server address: %s is inside the IP cidr: %s\n", server_address_clean, noproxy_item);
1501 }
1502 break;
1503 }
1504 }
1505
1506 noproxy_item = strtok(NULL, ",");
1507 }
1508
1509 free(curlopt_noproxy_copy);
1510 }
1511
1512 if (working_state.curlopt_proxy != NULL){
1513 // Libcurl documentation
1514 // Setting the proxy string to "" (an empty string) explicitly disables the use of a proxy, even if there is an environment variable set for it.
1515 if ( strlen(working_state.curlopt_proxy) == 0){
1516 return 0;
1517 }
1518
1519 if ( strncmp( working_state.curlopt_proxy, "http://", 7) == 0){
1520 if (verbose >= 1){
1521 printf("* proxy scheme is http, proxy: %s resolves host: %s or server_address: %s\n", working_state.curlopt_proxy, host_name_display, server_address_clean);
1522 }
1523 free(server_address_clean);
1524 return 1;
1525 }
1526
1527 if ( strncmp( working_state.curlopt_proxy, "https://", 8) == 0){
1528 if (verbose >= 1){
1529 printf("* proxy scheme is https, proxy: %s resolves host: %s or server_address: %s\n", working_state.curlopt_proxy, host_name_display, server_address_clean);
1530 }
1531 free(server_address_clean);
1532 return 1;
1533 }
1534
1535 if ( strncmp( working_state.curlopt_proxy, "socks4://", 9) == 0){
1536 if (verbose >= 1){
1537 printf("* proxy scheme is socks, proxy: %s does not resolve host: %s or server_address: %s\n", working_state.curlopt_proxy, host_name_display, server_address_clean);
1538 }
1539 free(server_address_clean);
1540 return 0;
1541 }
1542
1543 if ( strncmp( working_state.curlopt_proxy, "socks4a://", 10) == 0){
1544 if (verbose >= 1){
1545 printf("* proxy scheme is socks4a, proxy: %s resolves host: %s or server_address: %s\n", working_state.curlopt_proxy, host_name_display, server_address_clean);
1546 }
1547 free(server_address_clean);
1548 return 1;
1549 }
1550
1551 if ( strncmp( working_state.curlopt_proxy, "socks5://", 9) == 0){
1552 if (verbose >= 1){
1553 printf("* proxy scheme is socks5, proxy: %s does not resolve host: %s or server_address: %s\n", working_state.curlopt_proxy, host_name_display, server_address_clean);
1554 }
1555 free(server_address_clean);
1556 return 0;
1557 }
1558
1559 if ( strncmp( working_state.curlopt_proxy, "socks5h://", 10) == 0){
1560 if (verbose >= 1){
1561 printf("* proxy scheme is socks5h, proxy: %s resolves host: %s or server_address: %s\n", working_state.curlopt_proxy, host_name_display, server_address_clean);
1562 }
1563 free(server_address_clean);
1564 return 1;
1565 }
1566
1567 // Libcurl documentation:
1568 // Without a scheme prefix, CURLOPT_PROXYTYPE can be used to specify which kind of proxy the string identifies.
1569 // We do not set this value
1570 // Without a scheme, it is treated as an http proxy
1571
1572 return 1;
1573 }
1574
1575 if (verbose >= 1){
1576 printf("* proxy scheme is unknown/unavailable, no proxy is assumed for host: %s or server_address: %s\n", host_name_display, server_address_clean);
1577 }
1578
1579 free(server_address_clean);
1580 return 0;
1581}
1582
1583int ip_addr_inside_cidr(const char* cidr_region_or_ip_addr, const char* target_ip){
1584 unsigned int slash_count = 0;
1585 unsigned int last_slash_idx = 0;
1586 for(size_t i = 0; i < strlen(cidr_region_or_ip_addr); i++){
1587 if(cidr_region_or_ip_addr[i] == '/'){
1588 slash_count++;
1589 last_slash_idx = (unsigned int)i;
1590 }
1591 }
1592
1593 char *cidr_ip_part = NULL;
1594 int prefix_length = 0;
1595
1596 if (slash_count == 0) {
1597 cidr_ip_part = strdup(cidr_region_or_ip_addr);
1598 if (!cidr_ip_part) return -1;
1599 } else if (slash_count == 1) {
1600 cidr_ip_part = strndup(cidr_region_or_ip_addr, last_slash_idx);
1601 if (!cidr_ip_part) return -1;
1602
1603 errno = 0;
1604 long long tmp = strtoll(cidr_region_or_ip_addr + last_slash_idx + 1, NULL, 10);
1605 if (errno == ERANGE) {
1606 if (verbose >= 1) {
1607 printf("cidr_region_or_ip: %s , could not parse subnet length\n", cidr_region_or_ip_addr);
1608 }
1609 free(cidr_ip_part);
1610 return -1;
1611 }
1612 prefix_length = (int)tmp;
1613 } else {
1614 printf("cidr_region_or_ip: %s , has %d number of '/' characters, is not a valid cidr_region or IP\n", cidr_region_or_ip_addr, slash_count);
1615 return -1;
1616 }
1617
1618 int cidr_addr_family, target_addr_family;
1619 if (strchr(cidr_ip_part, ':')){
1620 cidr_addr_family = AF_INET6;
1621 } else {
1622 cidr_addr_family = AF_INET;
1623 }
1624
1625 if (strchr(target_ip, ':')){
1626 target_addr_family = AF_INET6;
1627 } else {
1628 target_addr_family = AF_INET;
1629 }
1630
1631 if (cidr_addr_family != target_addr_family){
1632 if (verbose >= 1){
1633 printf("cidr address: %s and target ip address: %s have different address families\n", cidr_ip_part, target_ip);
1634 }
1635 free(cidr_ip_part);
1636 return 0;
1637 }
1638
1639 // If no prefix is given, treat the cidr as a single address (full-length prefix)
1640 if (slash_count == 0) {
1641 prefix_length = (cidr_addr_family == AF_INET) ? 32 : 128;
1642 }
1643
1644 int max_bits = (cidr_addr_family == AF_INET) ? 32u : 128u;
1645 if (prefix_length < 0 || prefix_length > max_bits) {
1646 if (verbose >= 1) {
1647 printf("cidr_region_or_ip: %s has invalid prefix length: %u\n", cidr_region_or_ip_addr, prefix_length);
1648 }
1649 free(cidr_ip_part);
1650 return -1;
1651 }
1652
1653 if (verbose >= 1){
1654 printf("cidr_region_or_ip: %s , has prefix length: %u\n", cidr_region_or_ip_addr, prefix_length);
1655 }
1656
1657 int inet_pton_rc;
1658 uint8_t *cidr_bytes = NULL;
1659 uint8_t *target_bytes = NULL;
1660 uint8_t cidr_buf[16];
1661 uint8_t target_buf[16];
1662 size_t total_bytes = 0;
1663
1664 if (cidr_addr_family == AF_INET) {
1665 struct in_addr cidr_ipv4;
1666 struct in_addr target_ipv4;
1667 inet_pton_rc = inet_pton(AF_INET, cidr_ip_part, &cidr_ipv4);
1668 if (inet_pton_rc != 1) {
1669 if (verbose >= 1) {
1670 printf("ip string: %s contains characters not valid for its address family: IPv4\n", cidr_ip_part);
1671 }
1672 free(cidr_ip_part);
1673 return -1;
1674 }
1675 inet_pton_rc = inet_pton(AF_INET, target_ip, &target_ipv4);
1676 if (inet_pton_rc != 1) {
1677 if (verbose >= 1) {
1678 printf("ip string: %s contains characters not valid for its address family: IPv4\n", target_ip);
1679 }
1680 free(cidr_ip_part);
1681 return -1;
1682 }
1683 // copy the addresses in network byte order to a buffer for comparison
1684 memcpy(cidr_buf, &cidr_ipv4.s_addr, 4);
1685 memcpy(target_buf, &target_ipv4.s_addr, 4);
1686 cidr_bytes = cidr_buf;
1687 target_bytes = target_buf;
1688 total_bytes = 4;
1689 } else {
1690 struct in6_addr cidr_ipv6;
1691 struct in6_addr target_ipv6;
1692 inet_pton_rc = inet_pton(AF_INET6, cidr_ip_part, &cidr_ipv6);
1693 if (inet_pton_rc != 1) {
1694 if (verbose >= 1) {
1695 printf("ip string: %s contains characters not valid for its address family: IPv6\n", cidr_ip_part);
1696 }
1697 free(cidr_ip_part);
1698 return -1;
1699 }
1700 inet_pton_rc = inet_pton(AF_INET6, target_ip, &target_ipv6);
1701 if (inet_pton_rc != 1) {
1702 if (verbose >= 1) {
1703 printf("ip string: %s contains characters not valid for its address family: IPv6\n", target_ip);
1704 }
1705 free(cidr_ip_part);
1706 return -1;
1707 }
1708 memcpy(cidr_buf, &cidr_ipv6, 16);
1709 memcpy(target_buf, &target_ipv6, 16);
1710 cidr_bytes = cidr_buf;
1711 target_bytes = target_buf;
1712 total_bytes = 16;
1713 }
1714
1715 int prefix_bytes = prefix_length / 8;
1716 int prefix_bits = prefix_length % 8;
1717
1718 if (prefix_bytes > 0) {
1719 if (memcmp(cidr_bytes, target_bytes, (size_t)prefix_bytes) != 0) {
1720 if (verbose >= 1) {
1721 printf("the first %d bytes of the cidr_region_or_ip: %s and target_ip: %s are different\n", prefix_bytes, cidr_ip_part, target_ip);
1722 }
1723 free(cidr_ip_part);
1724 return 0;
1725 }
1726 }
1727
1728 if (prefix_bits != 0) {
1729 uint8_t cidr_oct = cidr_bytes[prefix_bytes];
1730 uint8_t target_oct = target_bytes[prefix_bytes];
1731 // the mask has first prefix_bits bits 1, the rest as 0
1732 uint8_t mask = (uint8_t)(0xFFu << (8 - prefix_bits));
1733 if ((cidr_oct & mask) != (target_oct & mask)) {
1734 if (verbose >= 1) {
1735 printf("looking at the last %d bits of the prefix, cidr_region_or_ip(%s) byte is: %u and target_ip byte(%s) is: %u, applying bitmask: %02X returns different results\n", prefix_bits, cidr_ip_part, (unsigned)cidr_oct, target_ip, (unsigned)target_oct, mask);
1736 }
1737 free(cidr_ip_part);
1738 return 0;
1739 }
1740 }
1741
1742 free(cidr_ip_part);
1743 return 1;
1744}
diff --git a/plugins/check_curl.d/check_curl_helpers.h b/plugins/check_curl.d/check_curl_helpers.h
index e7b80f7e..cc47bf9d 100644
--- a/plugins/check_curl.d/check_curl_helpers.h
+++ b/plugins/check_curl.d/check_curl_helpers.h
@@ -80,7 +80,7 @@ check_curl_configure_curl_wrapper check_curl_configure_curl(check_curl_static_cu
80 check_curl_working_state working_state, 80 check_curl_working_state working_state,
81 bool check_cert, 81 bool check_cert,
82 bool on_redirect_dependent, 82 bool on_redirect_dependent,
83 int follow_method, int max_depth); 83 int follow_method, long max_depth);
84 84
85void handle_curl_option_return_code(CURLcode res, const char *option); 85void handle_curl_option_return_code(CURLcode res, const char *option);
86 86
@@ -126,3 +126,12 @@ void test_file(char *path);
126mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_till_exp, 126mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_till_exp,
127 int crit_days_till_exp); 127 int crit_days_till_exp);
128char *fmt_url(check_curl_working_state workingState); 128char *fmt_url(check_curl_working_state workingState);
129
130
131/* function that will determine if the host or the proxy resolves the target hostname
132returns 0 if requester resolves the hostname locally, 1 if proxy resolves the hostname */
133int determine_hostname_resolver(const check_curl_working_state working_state, const check_curl_static_curl_config config);
134
135/* Checks if an IP is inside given CIDR region. Using /protocol_size or not specifying the prefix length performs an equality check. Supports both IPv4 and IPv6
136returns 1 if the target_ip address is inside the given cidr_region_or_ip_addr, 0 if its out. return codes < 0 mean an error has occurred. */
137int ip_addr_inside_cidr(const char* cidr_region_or_ip_addr, const char* target_ip);
diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h
index f51b2ee9..bcdf3010 100644
--- a/plugins/check_curl.d/config.h
+++ b/plugins/check_curl.d/config.h
@@ -48,6 +48,11 @@ typedef struct {
48 48
49 bool use_ssl; 49 bool use_ssl;
50 bool no_body; 50 bool no_body;
51
52 /* curl CURLOPT_PROXY option will be set to this value if not NULL */
53 char *curlopt_proxy;
54 /* curl CURLOPT_NOPROXY option will be set to this value if not NULL */
55 char *curlopt_noproxy;
51} check_curl_working_state; 56} check_curl_working_state;
52 57
53check_curl_working_state check_curl_working_state_init(); 58check_curl_working_state check_curl_working_state_init();
@@ -57,14 +62,16 @@ typedef struct {
57 bool haproxy_protocol; 62 bool haproxy_protocol;
58 long socket_timeout; 63 long socket_timeout;
59 sa_family_t sin_family; 64 sa_family_t sin_family;
60 int curl_http_version; 65 long curl_http_version;
61 char **http_opt_headers; 66 char **http_opt_headers;
62 size_t http_opt_headers_count; 67 size_t http_opt_headers_count;
63 int ssl_version; 68 long ssl_version;
64 char *client_cert; 69 char *client_cert;
65 char *client_privkey; 70 char *client_privkey;
66 char *ca_cert; 71 char *ca_cert;
67 bool verify_peer_and_host; 72 bool verify_peer_and_host;
73 char proxy[DEFAULT_BUFFER_SIZE];
74 char no_proxy[DEFAULT_BUFFER_SIZE];
68 char user_agent[DEFAULT_BUFFER_SIZE]; 75 char user_agent[DEFAULT_BUFFER_SIZE];
69 char proxy_auth[MAX_INPUT_BUFFER]; 76 char proxy_auth[MAX_INPUT_BUFFER];
70 char user_auth[MAX_INPUT_BUFFER]; 77 char user_auth[MAX_INPUT_BUFFER];
@@ -76,7 +83,7 @@ typedef struct {
76 check_curl_working_state initial_config; 83 check_curl_working_state initial_config;
77 84
78 check_curl_static_curl_config curl_config; 85 check_curl_static_curl_config curl_config;
79 int max_depth; 86 long max_depth;
80 int followmethod; 87 int followmethod;
81 int followsticky; 88 int followsticky;
82 89
diff --git a/plugins/check_dbi.c b/plugins/check_dbi.c
index 81d92952..dd466d00 100644
--- a/plugins/check_dbi.c
+++ b/plugins/check_dbi.c
@@ -470,7 +470,7 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) {
470 470
471 case 'c': /* critical range */ { 471 case 'c': /* critical range */ {
472 mp_range_parsed tmp = mp_parse_range_string(optarg); 472 mp_range_parsed tmp = mp_parse_range_string(optarg);
473 if (tmp.error != MP_PARSING_SUCCES) { 473 if (tmp.error != MP_PARSING_SUCCESS) {
474 die(STATE_UNKNOWN, "failed to parse critical threshold"); 474 die(STATE_UNKNOWN, "failed to parse critical threshold");
475 } 475 }
476 result.config.thresholds = mp_thresholds_set_crit(result.config.thresholds, tmp.range); 476 result.config.thresholds = mp_thresholds_set_crit(result.config.thresholds, tmp.range);
@@ -478,7 +478,7 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) {
478 } break; 478 } break;
479 case 'w': /* warning range */ { 479 case 'w': /* warning range */ {
480 mp_range_parsed tmp = mp_parse_range_string(optarg); 480 mp_range_parsed tmp = mp_parse_range_string(optarg);
481 if (tmp.error != MP_PARSING_SUCCES) { 481 if (tmp.error != MP_PARSING_SUCCESS) {
482 die(STATE_UNKNOWN, "failed to parse warning threshold"); 482 die(STATE_UNKNOWN, "failed to parse warning threshold");
483 } 483 }
484 result.config.thresholds = mp_thresholds_set_warn(result.config.thresholds, tmp.range); 484 result.config.thresholds = mp_thresholds_set_warn(result.config.thresholds, tmp.range);
diff --git a/plugins/check_disk.c b/plugins/check_disk.c
index d42b5486..0d941f25 100644
--- a/plugins/check_disk.c
+++ b/plugins/check_disk.c
@@ -838,7 +838,7 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) {
838 } 838 }
839 char *range = argv[index++]; 839 char *range = argv[index++];
840 mp_range_parsed tmp = mp_parse_range_string(range); 840 mp_range_parsed tmp = mp_parse_range_string(range);
841 if (tmp.error != MP_PARSING_SUCCES) { 841 if (tmp.error != MP_PARSING_SUCCESS) {
842 die(STATE_UNKNOWN, "failed to parse warning threshold"); 842 die(STATE_UNKNOWN, "failed to parse warning threshold");
843 } 843 }
844 844
@@ -859,7 +859,7 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) {
859 } 859 }
860 char *range = argv[index++]; 860 char *range = argv[index++];
861 mp_range_parsed tmp = mp_parse_range_string(range); 861 mp_range_parsed tmp = mp_parse_range_string(range);
862 if (tmp.error != MP_PARSING_SUCCES) { 862 if (tmp.error != MP_PARSING_SUCCESS) {
863 die(STATE_UNKNOWN, "failed to parse warning threshold"); 863 die(STATE_UNKNOWN, "failed to parse warning threshold");
864 } 864 }
865 865
@@ -1262,6 +1262,10 @@ mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, bool display_
1262 double free_inode_percentage = 1262 double free_inode_percentage =
1263 calculate_percent(measurement_unit.inodes_free, measurement_unit.inodes_total); 1263 calculate_percent(measurement_unit.inodes_free, measurement_unit.inodes_total);
1264 1264
1265 mp_perfdata inode_percentage_pd = perfdata_init();
1266 inode_percentage_pd = mp_set_pd_value(inode_percentage_pd, free_inode_percentage);
1267 inode_percentage_pd = mp_pd_set_thresholds(inode_percentage_pd, measurement_unit.freeinodes_percent_thresholds);
1268
1265 if (verbose > 0) { 1269 if (verbose > 0) {
1266 printf("free inode percentage computed: %g\n", free_inode_percentage); 1270 printf("free inode percentage computed: %g\n", free_inode_percentage);
1267 } 1271 }
@@ -1293,7 +1297,7 @@ mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, bool display_
1293 inodes_pd = mp_pd_set_thresholds(inodes_pd, absolut_inode_thresholds); 1297 inodes_pd = mp_pd_set_thresholds(inodes_pd, absolut_inode_thresholds);
1294 1298
1295 freeindodes_percent_sc = 1299 freeindodes_percent_sc =
1296 mp_set_subcheck_state(freeindodes_percent_sc, mp_get_pd_status(inodes_pd)); 1300 mp_set_subcheck_state(freeindodes_percent_sc, mp_get_pd_status(inode_percentage_pd));
1297 if (display_inodes_perfdata) { 1301 if (display_inodes_perfdata) {
1298 mp_add_perfdata_to_subcheck(&freeindodes_percent_sc, inodes_pd); 1302 mp_add_perfdata_to_subcheck(&freeindodes_percent_sc, inodes_pd);
1299 } 1303 }
diff --git a/plugins/check_http.c b/plugins/check_http.c
index d264b95d..71f94b91 100644
--- a/plugins/check_http.c
+++ b/plugins/check_http.c
@@ -544,11 +544,7 @@ bool process_arguments(int argc, char **argv) {
544 address_family = AF_INET; 544 address_family = AF_INET;
545 break; 545 break;
546 case '6': 546 case '6':
547#ifdef USE_IPV6
548 address_family = AF_INET6; 547 address_family = AF_INET6;
549#else
550 usage4(_("IPv6 support not available"));
551#endif
552 break; 548 break;
553 case 'v': /* verbose */ 549 case 'v': /* verbose */
554 verbose = true; 550 verbose = true;
@@ -1036,7 +1032,7 @@ int check_http(void) {
1036 printf("SSL initialized\n"); 1032 printf("SSL initialized\n");
1037 } 1033 }
1038 if (result != STATE_OK) { 1034 if (result != STATE_OK) {
1039 die(STATE_CRITICAL, NULL); 1035 die(STATE_CRITICAL, _("HTTP CRITICAL - SSL error\n"));
1040 } 1036 }
1041 microsec_ssl = deltime(tv_temp); 1037 microsec_ssl = deltime(tv_temp);
1042 elapsed_time_ssl = (double)microsec_ssl / 1.0e6; 1038 elapsed_time_ssl = (double)microsec_ssl / 1.0e6;
diff --git a/plugins/check_ide_smart.c b/plugins/check_ide_smart.c
index 16fe3d01..c1325cf9 100644
--- a/plugins/check_ide_smart.c
+++ b/plugins/check_ide_smart.c
@@ -39,6 +39,8 @@ const char *email = "devel@monitoring-plugins.org";
39 39
40#include "common.h" 40#include "common.h"
41#include "utils.h" 41#include "utils.h"
42#include "check_ide_smart.d/config.h"
43#include "states.h"
42 44
43static void print_help(void); 45static void print_help(void);
44void print_usage(void); 46void print_usage(void);
@@ -46,6 +48,7 @@ void print_usage(void);
46#include <sys/stat.h> 48#include <sys/stat.h>
47#include <sys/ioctl.h> 49#include <sys/ioctl.h>
48#include <fcntl.h> 50#include <fcntl.h>
51
49#ifdef __linux__ 52#ifdef __linux__
50# include <linux/hdreg.h> 53# include <linux/hdreg.h>
51# include <linux/types.h> 54# include <linux/types.h>
@@ -77,30 +80,30 @@ void print_usage(void);
77#define OPERATIONAL 0 80#define OPERATIONAL 0
78#define UNKNOWN -1 81#define UNKNOWN -1
79 82
80typedef struct threshold_s { 83typedef struct {
81 uint8_t id; 84 uint8_t id;
82 uint8_t threshold; 85 uint8_t threshold;
83 uint8_t reserved[10]; 86 uint8_t reserved[10];
84} __attribute__((packed)) threshold_t; 87} __attribute__((packed)) smart_threshold;
85 88
86typedef struct thresholds_s { 89typedef struct {
87 uint16_t revision; 90 uint16_t revision;
88 threshold_t thresholds[NR_ATTRIBUTES]; 91 smart_threshold thresholds[NR_ATTRIBUTES];
89 uint8_t reserved[18]; 92 uint8_t reserved[18];
90 uint8_t vendor[131]; 93 uint8_t vendor[131];
91 uint8_t checksum; 94 uint8_t checksum;
92} __attribute__((packed)) thresholds_t; 95} __attribute__((packed)) smart_thresholds;
93 96
94typedef struct value_s { 97typedef struct {
95 uint8_t id; 98 uint8_t id;
96 uint16_t status; 99 uint16_t status;
97 uint8_t value; 100 uint8_t value;
98 uint8_t vendor[8]; 101 uint8_t vendor[8];
99} __attribute__((packed)) value_t; 102} __attribute__((packed)) smart_value;
100 103
101typedef struct values_s { 104typedef struct {
102 uint16_t revision; 105 uint16_t revision;
103 value_t values[NR_ATTRIBUTES]; 106 smart_value values[NR_ATTRIBUTES];
104 uint8_t offline_status; 107 uint8_t offline_status;
105 uint8_t vendor1; 108 uint8_t vendor1;
106 uint16_t offline_timeout; 109 uint16_t offline_timeout;
@@ -110,7 +113,7 @@ typedef struct values_s {
110 uint8_t reserved[16]; 113 uint8_t reserved[16];
111 uint8_t vendor[125]; 114 uint8_t vendor[125];
112 uint8_t checksum; 115 uint8_t checksum;
113} __attribute__((packed)) values_t; 116} __attribute__((packed)) smart_values;
114 117
115static struct { 118static struct {
116 uint8_t value; 119 uint8_t value;
@@ -133,25 +136,20 @@ enum SmartCommand {
133 SMART_CMD_AUTO_OFFLINE 136 SMART_CMD_AUTO_OFFLINE
134}; 137};
135 138
136static char *get_offline_text(int); 139static char *get_offline_text(int /*status*/);
137static int smart_read_values(int, values_t *); 140static int smart_read_values(int /*fd*/, smart_values * /*values*/);
138static int nagios(values_t *, thresholds_t *); 141static mp_state_enum compare_values_and_thresholds(smart_values * /*p*/, smart_thresholds * /*t*/);
139static void print_value(value_t *, threshold_t *); 142static void print_value(smart_value * /*p*/, smart_threshold * /*t*/);
140static void print_values(values_t *, thresholds_t *); 143static void print_values(smart_values * /*p*/, smart_thresholds * /*t*/);
141static int smart_cmd_simple(int, enum SmartCommand, uint8_t, bool); 144static mp_state_enum smart_cmd_simple(int /*fd*/, enum SmartCommand /*command*/, uint8_t /*val0*/, bool /*show_error*/);
142static int smart_read_thresholds(int, thresholds_t *); 145static int smart_read_thresholds(int /*fd*/, smart_thresholds * /*thresholds*/);
143static bool verbose = false; 146static int verbose = 0;
144 147
145int main(int argc, char *argv[]) { 148typedef struct {
146 char *device = NULL; 149 int errorcode;
147 int o; 150 check_ide_smart_config config;
148 int longindex; 151} check_ide_smart_config_wrapper;
149 int retval = 0; 152static check_ide_smart_config_wrapper process_arguments(int argc, char **argv) {
150
151 thresholds_t thresholds;
152 values_t values;
153 int fd;
154
155 static struct option longopts[] = {{"device", required_argument, 0, 'd'}, 153 static struct option longopts[] = {{"device", required_argument, 0, 'd'},
156 {"immediate", no_argument, 0, 'i'}, 154 {"immediate", no_argument, 0, 'i'},
157 {"quiet-check", no_argument, 0, 'q'}, 155 {"quiet-check", no_argument, 0, 'q'},
@@ -162,24 +160,22 @@ int main(int argc, char *argv[]) {
162 {"version", no_argument, 0, 'V'}, 160 {"version", no_argument, 0, 'V'},
163 {0, 0, 0, 0}}; 161 {0, 0, 0, 0}};
164 162
165 /* Parse extra opts if any */ 163 check_ide_smart_config_wrapper result = {
166 argv = np_extra_opts(&argc, argv, progname); 164 .errorcode = OK,
167 165 .config = check_ide_smart_init(),
168 setlocale(LC_ALL, ""); 166 };
169 bindtextdomain(PACKAGE, LOCALEDIR);
170 textdomain(PACKAGE);
171 167
172 while (true) { 168 while (true) {
169 int longindex = 0;
170 int option_index = getopt_long(argc, argv, "+d:iq10nhVv", longopts, &longindex);
173 171
174 o = getopt_long(argc, argv, "+d:iq10nhVv", longopts, &longindex); 172 if (option_index == -1 || option_index == EOF || option_index == 1) {
175
176 if (o == -1 || o == EOF || o == 1) {
177 break; 173 break;
178 } 174 }
179 175
180 switch (o) { 176 switch (option_index) {
181 case 'd': 177 case 'd':
182 device = optarg; 178 result.config.device = optarg;
183 break; 179 break;
184 case 'q': 180 case 'q':
185 fprintf(stderr, "%s\n", _("DEPRECATION WARNING: the -q switch (quiet output) is no longer \"quiet\".")); 181 fprintf(stderr, "%s\n", _("DEPRECATION WARNING: the -q switch (quiet output) is no longer \"quiet\"."));
@@ -189,84 +185,103 @@ int main(int argc, char *argv[]) {
189 case '1': 185 case '1':
190 case '0': 186 case '0':
191 printf("%s\n", _("SMART commands are broken and have been disabled (See Notes in --help).")); 187 printf("%s\n", _("SMART commands are broken and have been disabled (See Notes in --help)."));
192 return STATE_CRITICAL; 188 result.errorcode = ERROR;
189 return result;
193 break; 190 break;
194 case 'n': 191 case 'n':
195 fprintf(stderr, "%s\n", _("DEPRECATION WARNING: the -n switch (Nagios-compatible output) is now the")); 192 fprintf(stderr, "%s\n", _("DEPRECATION WARNING: the -n switch (Nagios-compatible output) is now the"));
196 fprintf(stderr, "%s\n", _("default and will be removed from future releases.")); 193 fprintf(stderr, "%s\n", _("default and will be removed from future releases."));
197 break; 194 break;
198 case 'v': /* verbose */ 195 case 'v': /* verbose */
199 verbose = true; 196 verbose++;
200 break; 197 break;
201 case 'h': 198 case 'h':
202 print_help(); 199 print_help();
203 return STATE_UNKNOWN; 200 exit(STATE_UNKNOWN);
204 case 'V': 201 case 'V':
205 print_revision(progname, NP_VERSION); 202 print_revision(progname, NP_VERSION);
206 return STATE_UNKNOWN; 203 exit(STATE_UNKNOWN);
207 default: 204 default:
208 usage5(); 205 usage5();
209 } 206 }
210 } 207 }
211 208
212 if (optind < argc) { 209 if (optind < argc) {
213 device = argv[optind]; 210 result.config.device = argv[optind];
214 } 211 }
215 212
216 if (!device) { 213 if (result.config.device == NULL) {
217 print_help(); 214 print_help();
218 return STATE_UNKNOWN; 215 exit(STATE_UNKNOWN);
216 }
217
218 return result;
219}
220
221int main(int argc, char *argv[]) {
222 setlocale(LC_ALL, "");
223 bindtextdomain(PACKAGE, LOCALEDIR);
224 textdomain(PACKAGE);
225
226 /* Parse extra opts if any */
227 argv = np_extra_opts(&argc, argv, progname);
228
229 check_ide_smart_config_wrapper tmp_config = process_arguments(argc, argv);
230
231 if (tmp_config.errorcode != OK) {
232 die(STATE_UNKNOWN, _("Failed to parse commandline"));
219 } 233 }
220 234
221 fd = open(device, OPEN_MODE); 235 check_ide_smart_config config = tmp_config.config;
222 236
223 if (fd < 0) { 237 int device_file_descriptor = open(config.device, OPEN_MODE);
224 printf(_("CRITICAL - Couldn't open device %s: %s\n"), device, strerror(errno)); 238
239 if (device_file_descriptor < 0) {
240 printf(_("CRITICAL - Couldn't open device %s: %s\n"), config.device, strerror(errno));
225 return STATE_CRITICAL; 241 return STATE_CRITICAL;
226 } 242 }
227 243
228 if (smart_cmd_simple(fd, SMART_CMD_ENABLE, 0, false)) { 244 if (smart_cmd_simple(device_file_descriptor, SMART_CMD_ENABLE, 0, false)) {
229 printf(_("CRITICAL - SMART_CMD_ENABLE\n")); 245 printf(_("CRITICAL - SMART_CMD_ENABLE\n"));
230 return STATE_CRITICAL; 246 return STATE_CRITICAL;
231 } 247 }
232 248
233 smart_read_values(fd, &values); 249 smart_values values;
234 smart_read_thresholds(fd, &thresholds); 250 smart_read_values(device_file_descriptor, &values);
235 retval = nagios(&values, &thresholds); 251 smart_thresholds thresholds;
252 smart_read_thresholds(device_file_descriptor, &thresholds);
253 mp_state_enum retval = compare_values_and_thresholds(&values, &thresholds);
236 if (verbose) { 254 if (verbose) {
237 print_values(&values, &thresholds); 255 print_values(&values, &thresholds);
238 } 256 }
239 257
240 close(fd); 258 close(device_file_descriptor);
241 return retval; 259 return retval;
242} 260}
243 261
244char *get_offline_text(int status) { 262char *get_offline_text(int status) {
245 int i; 263 for (int index = 0; offline_status_text[index].text; index++) {
246 for (i = 0; offline_status_text[i].text; i++) { 264 if (offline_status_text[index].value == status) {
247 if (offline_status_text[i].value == status) { 265 return offline_status_text[index].text;
248 return offline_status_text[i].text;
249 } 266 }
250 } 267 }
251 return "UNKNOWN"; 268 return "UNKNOWN";
252} 269}
253 270
254int smart_read_values(int fd, values_t *values) { 271int smart_read_values(int file_descriptor, smart_values *values) {
255#ifdef __linux__ 272#ifdef __linux__
256 int e;
257 uint8_t args[4 + 512]; 273 uint8_t args[4 + 512];
258 args[0] = WIN_SMART; 274 args[0] = WIN_SMART;
259 args[1] = 0; 275 args[1] = 0;
260 args[2] = SMART_READ_VALUES; 276 args[2] = SMART_READ_VALUES;
261 args[3] = 1; 277 args[3] = 1;
262 if (ioctl(fd, HDIO_DRIVE_CMD, &args)) { 278 if (ioctl(file_descriptor, HDIO_DRIVE_CMD, &args)) {
263 e = errno; 279 int errno_storage = errno;
264 printf(_("CRITICAL - SMART_READ_VALUES: %s\n"), strerror(errno)); 280 printf(_("CRITICAL - SMART_READ_VALUES: %s\n"), strerror(errno));
265 return e; 281 return errno_storage;
266 } 282 }
267 memcpy(values, args + 4, 512); 283 memcpy(values, args + 4, 512);
268#endif /* __linux__ */ 284#elif defined __NetBSD__
269#ifdef __NetBSD__
270 struct atareq req; 285 struct atareq req;
271 unsigned char inbuf[DEV_BSIZE]; 286 unsigned char inbuf[DEV_BSIZE];
272 287
@@ -281,34 +296,37 @@ int smart_read_values(int fd, values_t *values) {
281 req.datalen = sizeof(inbuf); 296 req.datalen = sizeof(inbuf);
282 req.cylinder = WDSMART_CYL; 297 req.cylinder = WDSMART_CYL;
283 298
284 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { 299 if (ioctl(file_descriptor, ATAIOCCOMMAND, &req) == 0) {
285 if (req.retsts != ATACMD_OK) { 300 if (req.retsts != ATACMD_OK) {
286 errno = ENODEV; 301 errno = ENODEV;
287 } 302 }
288 } 303 }
289 304
290 if (errno != 0) { 305 if (errno != 0) {
291 int e = errno; 306 int errno_storage = errno;
292 printf(_("CRITICAL - SMART_READ_VALUES: %s\n"), strerror(errno)); 307 printf(_("CRITICAL - SMART_READ_VALUES: %s\n"), strerror(errno));
293 return e; 308 return errno_storage;
294 } 309 }
295 310
296 (void)memcpy(values, inbuf, 512); 311 (void)memcpy(values, inbuf, 512);
297#endif /* __NetBSD__ */ 312#else // __linux__ || __NetBSD__
313# error Not implemented for this OS
314#endif
315
298 return 0; 316 return 0;
299} 317}
300 318
301int nagios(values_t *p, thresholds_t *t) { 319mp_state_enum compare_values_and_thresholds(smart_values *values, smart_thresholds *thresholds) {
302 value_t *value = p->values; 320 smart_value *value = values->values;
303 threshold_t *threshold = t->thresholds; 321 smart_threshold *threshold = thresholds->thresholds;
322
304 int status = OPERATIONAL; 323 int status = OPERATIONAL;
305 int prefailure = 0; 324 int prefailure = 0;
306 int advisory = 0; 325 int advisory = 0;
307 int failed = 0; 326 int failed = 0;
308 int passed = 0; 327 int passed = 0;
309 int total = 0; 328 int total = 0;
310 int i; 329 for (int i = 0; i < NR_ATTRIBUTES; i++) {
311 for (i = 0; i < NR_ATTRIBUTES; i++) {
312 if (value->id && threshold->id && value->id == threshold->id) { 330 if (value->id && threshold->id && value->id == threshold->id) {
313 if (value->value < threshold->threshold) { 331 if (value->value < threshold->threshold) {
314 ++failed; 332 ++failed;
@@ -327,6 +345,7 @@ int nagios(values_t *p, thresholds_t *t) {
327 ++value; 345 ++value;
328 ++threshold; 346 ++threshold;
329 } 347 }
348
330 switch (status) { 349 switch (status) {
331 case PREFAILURE: 350 case PREFAILURE:
332 printf(_("CRITICAL - %d Harddrive PreFailure%cDetected! %d/%d tests failed.\n"), prefailure, prefailure > 1 ? 's' : ' ', failed, 351 printf(_("CRITICAL - %d Harddrive PreFailure%cDetected! %d/%d tests failed.\n"), prefailure, prefailure > 1 ? 's' : ' ', failed,
@@ -349,50 +368,51 @@ int nagios(values_t *p, thresholds_t *t) {
349 return status; 368 return status;
350} 369}
351 370
352void print_value(value_t *p, threshold_t *t) { 371void print_value(smart_value *value_pointer, smart_threshold *threshold_pointer) {
353 printf("Id=%3d, Status=%2d {%s , %s}, Value=%3d, Threshold=%3d, %s\n", p->id, p->status, p->status & 1 ? "PreFailure" : "Advisory ", 372 printf("Id=%3d, Status=%2d {%s , %s}, Value=%3d, Threshold=%3d, %s\n", value_pointer->id, value_pointer->status,
354 p->status & 2 ? "OnLine " : "OffLine", p->value, t->threshold, p->value >= t->threshold ? "Passed" : "Failed"); 373 value_pointer->status & 1 ? "PreFailure" : "Advisory ", value_pointer->status & 2 ? "OnLine " : "OffLine",
374 value_pointer->value, threshold_pointer->threshold, value_pointer->value >= threshold_pointer->threshold ? "Passed" : "Failed");
355} 375}
356 376
357void print_values(values_t *p, thresholds_t *t) { 377void print_values(smart_values *values, smart_thresholds *thresholds) {
358 value_t *value = p->values; 378 smart_value *value = values->values;
359 threshold_t *threshold = t->thresholds; 379 smart_threshold *threshold = thresholds->thresholds;
360 int i; 380 for (int i = 0; i < NR_ATTRIBUTES; i++) {
361 for (i = 0; i < NR_ATTRIBUTES; i++) {
362 if (value->id && threshold->id && value->id == threshold->id) { 381 if (value->id && threshold->id && value->id == threshold->id) {
363 print_value(value++, threshold++); 382 print_value(value++, threshold++);
364 } 383 }
365 } 384 }
366 printf(_("OffLineStatus=%d {%s}, AutoOffLine=%s, OffLineTimeout=%d minutes\n"), p->offline_status, 385 printf(_("OffLineStatus=%d {%s}, AutoOffLine=%s, OffLineTimeout=%d minutes\n"), values->offline_status,
367 get_offline_text(p->offline_status & 0x7f), (p->offline_status & 0x80 ? "Yes" : "No"), p->offline_timeout / 60); 386 get_offline_text(values->offline_status & 0x7f), (values->offline_status & 0x80 ? "Yes" : "No"), values->offline_timeout / 60);
368 printf(_("OffLineCapability=%d {%s %s %s}\n"), p->offline_capability, p->offline_capability & 1 ? "Immediate" : "", 387 printf(_("OffLineCapability=%d {%s %s %s}\n"), values->offline_capability, values->offline_capability & 1 ? "Immediate" : "",
369 p->offline_capability & 2 ? "Auto" : "", p->offline_capability & 4 ? "AbortOnCmd" : "SuspendOnCmd"); 388 values->offline_capability & 2 ? "Auto" : "", values->offline_capability & 4 ? "AbortOnCmd" : "SuspendOnCmd");
370 printf(_("SmartRevision=%d, CheckSum=%d, SmartCapability=%d {%s %s}\n"), p->revision, p->checksum, p->smart_capability, 389 printf(_("SmartRevision=%d, CheckSum=%d, SmartCapability=%d {%s %s}\n"), values->revision, values->checksum, values->smart_capability,
371 p->smart_capability & 1 ? "SaveOnStandBy" : "", p->smart_capability & 2 ? "AutoSave" : ""); 390 values->smart_capability & 1 ? "SaveOnStandBy" : "", values->smart_capability & 2 ? "AutoSave" : "");
372} 391}
373 392
374int smart_cmd_simple(int fd, enum SmartCommand command, uint8_t val0, bool show_error) { 393mp_state_enum smart_cmd_simple(int file_descriptor, enum SmartCommand command, uint8_t val0, bool show_error) {
375 int e = STATE_UNKNOWN; 394 mp_state_enum result = STATE_UNKNOWN;
376#ifdef __linux__ 395#ifdef __linux__
377 uint8_t args[4]; 396 uint8_t args[4] = {
378 args[0] = WIN_SMART; 397 WIN_SMART,
379 args[1] = val0; 398 val0,
380 args[2] = smart_command[command].value; 399 smart_command[command].value,
381 args[3] = 0; 400 0,
382 if (ioctl(fd, HDIO_DRIVE_CMD, &args)) { 401 };
383 e = STATE_CRITICAL; 402
403 if (ioctl(file_descriptor, HDIO_DRIVE_CMD, &args)) {
404 result = STATE_CRITICAL;
384 if (show_error) { 405 if (show_error) {
385 printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno)); 406 printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno));
386 } 407 }
387 } else { 408 } else {
388 e = STATE_OK; 409 result = STATE_OK;
389 if (show_error) { 410 if (show_error) {
390 printf(_("OK - Command sent (%s)\n"), smart_command[command].text); 411 printf(_("OK - Command sent (%s)\n"), smart_command[command].text);
391 } 412 }
392 } 413 }
393 414
394#endif /* __linux__ */ 415#elif defined __NetBSD__
395#ifdef __NetBSD__
396 struct atareq req; 416 struct atareq req;
397 417
398 memset(&req, 0, sizeof(req)); 418 memset(&req, 0, sizeof(req));
@@ -403,7 +423,7 @@ int smart_cmd_simple(int fd, enum SmartCommand command, uint8_t val0, bool show_
403 req.cylinder = WDSMART_CYL; 423 req.cylinder = WDSMART_CYL;
404 req.sec_count = val0; 424 req.sec_count = val0;
405 425
406 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { 426 if (ioctl(file_descriptor, ATAIOCCOMMAND, &req) == 0) {
407 if (req.retsts != ATACMD_OK) { 427 if (req.retsts != ATACMD_OK) {
408 errno = ENODEV; 428 errno = ENODEV;
409 } 429 }
@@ -413,42 +433,42 @@ int smart_cmd_simple(int fd, enum SmartCommand command, uint8_t val0, bool show_
413 } 433 }
414 434
415 if (errno != 0) { 435 if (errno != 0) {
416 e = STATE_CRITICAL; 436 result = STATE_CRITICAL;
417 if (show_error) { 437 if (show_error) {
418 printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno)); 438 printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno));
419 } 439 }
420 } else { 440 } else {
421 e = STATE_OK; 441 result = STATE_OK;
422 if (show_error) { 442 if (show_error) {
423 printf(_("OK - Command sent (%s)\n"), smart_command[command].text); 443 printf(_("OK - Command sent (%s)\n"), smart_command[command].text);
424 } 444 }
425 } 445 }
426 446#else
447# error Not implemented for this OS
427#endif /* __NetBSD__ */ 448#endif /* __NetBSD__ */
428 return e; 449
450 return result;
429} 451}
430 452
431int smart_read_thresholds(int fd, thresholds_t *thresholds) { 453int smart_read_thresholds(int file_descriptor, smart_thresholds *thresholds) {
432#ifdef __linux__ 454#ifdef __linux__
433 int e;
434 uint8_t args[4 + 512]; 455 uint8_t args[4 + 512];
435 args[0] = WIN_SMART; 456 args[0] = WIN_SMART;
436 args[1] = 0; 457 args[1] = 0;
437 args[2] = SMART_READ_THRESHOLDS; 458 args[2] = SMART_READ_THRESHOLDS;
438 args[3] = 1; 459 args[3] = 1;
439 if (ioctl(fd, HDIO_DRIVE_CMD, &args)) { 460 if (ioctl(file_descriptor, HDIO_DRIVE_CMD, &args)) {
440 e = errno; 461 int errno_storage = errno;
441 printf(_("CRITICAL - SMART_READ_THRESHOLDS: %s\n"), strerror(errno)); 462 printf(_("CRITICAL - SMART_READ_THRESHOLDS: %s\n"), strerror(errno));
442 return e; 463 return errno_storage;
443 } 464 }
444 memcpy(thresholds, args + 4, 512); 465 memcpy(thresholds, args + 4, 512);
445#endif /* __linux__ */ 466#elif defined __NetBSD__
446#ifdef __NetBSD__
447 struct atareq req; 467 struct atareq req;
448 unsigned char inbuf[DEV_BSIZE];
449
450 memset(&req, 0, sizeof(req)); 468 memset(&req, 0, sizeof(req));
451 req.timeout = 1000; 469 req.timeout = 1000;
470
471 unsigned char inbuf[DEV_BSIZE];
452 memset(&inbuf, 0, sizeof(inbuf)); 472 memset(&inbuf, 0, sizeof(inbuf));
453 473
454 req.flags = ATACMD_READ; 474 req.flags = ATACMD_READ;
@@ -458,20 +478,23 @@ int smart_read_thresholds(int fd, thresholds_t *thresholds) {
458 req.datalen = sizeof(inbuf); 478 req.datalen = sizeof(inbuf);
459 req.cylinder = WDSMART_CYL; 479 req.cylinder = WDSMART_CYL;
460 480
461 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { 481 if (ioctl(file_descriptor, ATAIOCCOMMAND, &req) == 0) {
462 if (req.retsts != ATACMD_OK) { 482 if (req.retsts != ATACMD_OK) {
463 errno = ENODEV; 483 errno = ENODEV;
464 } 484 }
465 } 485 }
466 486
467 if (errno != 0) { 487 if (errno != 0) {
468 int e = errno; 488 int errno_storage = errno;
469 printf(_("CRITICAL - SMART_READ_THRESHOLDS: %s\n"), strerror(errno)); 489 printf(_("CRITICAL - SMART_READ_THRESHOLDS: %s\n"), strerror(errno));
470 return e; 490 return errno_storage;
471 } 491 }
472 492
473 (void)memcpy(thresholds, inbuf, 512); 493 (void)memcpy(thresholds, inbuf, 512);
494#else
495# error Not implemented for this OS
474#endif /* __NetBSD__ */ 496#endif /* __NetBSD__ */
497
475 return 0; 498 return 0;
476} 499}
477 500
diff --git a/plugins/check_ide_smart.d/config.h b/plugins/check_ide_smart.d/config.h
new file mode 100644
index 00000000..36899abe
--- /dev/null
+++ b/plugins/check_ide_smart.d/config.h
@@ -0,0 +1,15 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6typedef struct {
7 char *device;
8} check_ide_smart_config;
9
10check_ide_smart_config check_ide_smart_init() {
11 check_ide_smart_config tmp = {
12 .device = NULL,
13 };
14 return tmp;
15}
diff --git a/plugins/check_ldap.c b/plugins/check_ldap.c
index 1b2e2826..7f8282b4 100644
--- a/plugins/check_ldap.c
+++ b/plugins/check_ldap.c
@@ -400,7 +400,7 @@ check_ldap_config_wrapper process_arguments(int argc, char **argv) {
400 break; 400 break;
401 case 'w': { 401 case 'w': {
402 mp_range_parsed tmp = mp_parse_range_string(optarg); 402 mp_range_parsed tmp = mp_parse_range_string(optarg);
403 if (tmp.error != MP_PARSING_SUCCES) { 403 if (tmp.error != MP_PARSING_SUCCESS) {
404 die(STATE_UNKNOWN, "failed to parse warning connection time threshold"); 404 die(STATE_UNKNOWN, "failed to parse warning connection time threshold");
405 } 405 }
406 result.config.connection_time_threshold = 406 result.config.connection_time_threshold =
@@ -408,7 +408,7 @@ check_ldap_config_wrapper process_arguments(int argc, char **argv) {
408 } break; 408 } break;
409 case 'c': { 409 case 'c': {
410 mp_range_parsed tmp = mp_parse_range_string(optarg); 410 mp_range_parsed tmp = mp_parse_range_string(optarg);
411 if (tmp.error != MP_PARSING_SUCCES) { 411 if (tmp.error != MP_PARSING_SUCCESS) {
412 die(STATE_UNKNOWN, "failed to parse critical connection time threshold"); 412 die(STATE_UNKNOWN, "failed to parse critical connection time threshold");
413 } 413 }
414 result.config.connection_time_threshold = 414 result.config.connection_time_threshold =
@@ -416,7 +416,7 @@ check_ldap_config_wrapper process_arguments(int argc, char **argv) {
416 } break; 416 } break;
417 case 'W': { 417 case 'W': {
418 mp_range_parsed tmp = mp_parse_range_string(optarg); 418 mp_range_parsed tmp = mp_parse_range_string(optarg);
419 if (tmp.error != MP_PARSING_SUCCES) { 419 if (tmp.error != MP_PARSING_SUCCESS) {
420 die(STATE_UNKNOWN, "failed to parse number of entries warning threshold"); 420 die(STATE_UNKNOWN, "failed to parse number of entries warning threshold");
421 } 421 }
422 result.config.entries_thresholds = 422 result.config.entries_thresholds =
@@ -424,7 +424,7 @@ check_ldap_config_wrapper process_arguments(int argc, char **argv) {
424 } break; 424 } break;
425 case 'C': { 425 case 'C': {
426 mp_range_parsed tmp = mp_parse_range_string(optarg); 426 mp_range_parsed tmp = mp_parse_range_string(optarg);
427 if (tmp.error != MP_PARSING_SUCCES) { 427 if (tmp.error != MP_PARSING_SUCCESS) {
428 die(STATE_UNKNOWN, "failed to parse number of entries critical threshold"); 428 die(STATE_UNKNOWN, "failed to parse number of entries critical threshold");
429 } 429 }
430 result.config.entries_thresholds = 430 result.config.entries_thresholds =
@@ -462,11 +462,7 @@ check_ldap_config_wrapper process_arguments(int argc, char **argv) {
462 } 462 }
463 break; 463 break;
464 case '6': 464 case '6':
465#ifdef USE_IPV6
466 address_family = AF_INET6; 465 address_family = AF_INET6;
467#else
468 usage(_("IPv6 support not available\n"));
469#endif
470 break; 466 break;
471 case output_format_index: { 467 case output_format_index: {
472 parsed_output_format parser = mp_parse_output_format(optarg); 468 parsed_output_format parser = mp_parse_output_format(optarg);
diff --git a/plugins/check_mrtg.c b/plugins/check_mrtg.c
index cdc2a035..bb38fcc5 100644
--- a/plugins/check_mrtg.c
+++ b/plugins/check_mrtg.c
@@ -255,7 +255,7 @@ check_mrtg_config_wrapper process_arguments(int argc, char **argv) {
255 break; 255 break;
256 case 'w': /* critical time threshold */ { 256 case 'w': /* critical time threshold */ {
257 mp_range_parsed tmp = mp_parse_range_string(optarg); 257 mp_range_parsed tmp = mp_parse_range_string(optarg);
258 if (tmp.error != MP_PARSING_SUCCES) { 258 if (tmp.error != MP_PARSING_SUCCESS) {
259 die(STATE_UNKNOWN, "failed to parse warning threshold"); 259 die(STATE_UNKNOWN, "failed to parse warning threshold");
260 } 260 }
261 result.config.values_threshold = 261 result.config.values_threshold =
@@ -263,7 +263,7 @@ check_mrtg_config_wrapper process_arguments(int argc, char **argv) {
263 } break; 263 } break;
264 case 'c': /* warning time threshold */ { 264 case 'c': /* warning time threshold */ {
265 mp_range_parsed tmp = mp_parse_range_string(optarg); 265 mp_range_parsed tmp = mp_parse_range_string(optarg);
266 if (tmp.error != MP_PARSING_SUCCES) { 266 if (tmp.error != MP_PARSING_SUCCESS) {
267 die(STATE_UNKNOWN, "failed to parse critical threshold"); 267 die(STATE_UNKNOWN, "failed to parse critical threshold");
268 } 268 }
269 result.config.values_threshold = 269 result.config.values_threshold =
@@ -330,7 +330,7 @@ check_mrtg_config_wrapper process_arguments(int argc, char **argv) {
330 330
331 if (argc > option_char && !result.config.values_threshold.warning_is_set) { 331 if (argc > option_char && !result.config.values_threshold.warning_is_set) {
332 mp_range_parsed tmp = mp_parse_range_string(argv[option_char++]); 332 mp_range_parsed tmp = mp_parse_range_string(argv[option_char++]);
333 if (tmp.error != MP_PARSING_SUCCES) { 333 if (tmp.error != MP_PARSING_SUCCESS) {
334 die(STATE_UNKNOWN, "failed to parse warning threshold"); 334 die(STATE_UNKNOWN, "failed to parse warning threshold");
335 } 335 }
336 result.config.values_threshold = 336 result.config.values_threshold =
@@ -339,7 +339,7 @@ check_mrtg_config_wrapper process_arguments(int argc, char **argv) {
339 339
340 if (argc > option_char && !result.config.values_threshold.critical_is_set) { 340 if (argc > option_char && !result.config.values_threshold.critical_is_set) {
341 mp_range_parsed tmp = mp_parse_range_string(argv[option_char++]); 341 mp_range_parsed tmp = mp_parse_range_string(argv[option_char++]);
342 if (tmp.error != MP_PARSING_SUCCES) { 342 if (tmp.error != MP_PARSING_SUCCESS) {
343 die(STATE_UNKNOWN, "failed to parse critical threshold"); 343 die(STATE_UNKNOWN, "failed to parse critical threshold");
344 } 344 }
345 result.config.values_threshold = 345 result.config.values_threshold =
diff --git a/plugins/check_mysql.c b/plugins/check_mysql.c
index 26730d4c..15005bf5 100644
--- a/plugins/check_mysql.c
+++ b/plugins/check_mysql.c
@@ -572,7 +572,7 @@ check_mysql_config_wrapper process_arguments(int argc, char **argv) {
572 break; 572 break;
573 case 'w': { 573 case 'w': {
574 mp_range_parsed tmp = mp_parse_range_string(optarg); 574 mp_range_parsed tmp = mp_parse_range_string(optarg);
575 if (tmp.error != MP_PARSING_SUCCES) { 575 if (tmp.error != MP_PARSING_SUCCESS) {
576 die(STATE_UNKNOWN, "failed to parse warning time threshold"); 576 die(STATE_UNKNOWN, "failed to parse warning time threshold");
577 } 577 }
578 result.config.replica_thresholds = 578 result.config.replica_thresholds =
@@ -580,7 +580,7 @@ check_mysql_config_wrapper process_arguments(int argc, char **argv) {
580 } break; 580 } break;
581 case 'c': { 581 case 'c': {
582 mp_range_parsed tmp = mp_parse_range_string(optarg); 582 mp_range_parsed tmp = mp_parse_range_string(optarg);
583 if (tmp.error != MP_PARSING_SUCCES) { 583 if (tmp.error != MP_PARSING_SUCCESS) {
584 die(STATE_UNKNOWN, "failed to parse critical time threshold"); 584 die(STATE_UNKNOWN, "failed to parse critical time threshold");
585 } 585 }
586 result.config.replica_thresholds = 586 result.config.replica_thresholds =
diff --git a/plugins/check_mysql_query.c b/plugins/check_mysql_query.c
index ae6cc15d..fc0966d3 100644
--- a/plugins/check_mysql_query.c
+++ b/plugins/check_mysql_query.c
@@ -277,14 +277,14 @@ check_mysql_query_config_wrapper process_arguments(int argc, char **argv) {
277 break; 277 break;
278 case 'w': { 278 case 'w': {
279 mp_range_parsed tmp = mp_parse_range_string(optarg); 279 mp_range_parsed tmp = mp_parse_range_string(optarg);
280 if (tmp.error != MP_PARSING_SUCCES) { 280 if (tmp.error != MP_PARSING_SUCCESS) {
281 die(STATE_UNKNOWN, "failed to parse warning threshold"); 281 die(STATE_UNKNOWN, "failed to parse warning threshold");
282 } 282 }
283 result.config.thresholds = mp_thresholds_set_warn(result.config.thresholds, tmp.range); 283 result.config.thresholds = mp_thresholds_set_warn(result.config.thresholds, tmp.range);
284 } break; 284 } break;
285 case 'c': { 285 case 'c': {
286 mp_range_parsed tmp = mp_parse_range_string(optarg); 286 mp_range_parsed tmp = mp_parse_range_string(optarg);
287 if (tmp.error != MP_PARSING_SUCCES) { 287 if (tmp.error != MP_PARSING_SUCCESS) {
288 die(STATE_UNKNOWN, "failed to parse critical threshold"); 288 die(STATE_UNKNOWN, "failed to parse critical threshold");
289 } 289 }
290 result.config.thresholds = mp_thresholds_set_crit(result.config.thresholds, tmp.range); 290 result.config.thresholds = mp_thresholds_set_crit(result.config.thresholds, tmp.range);
diff --git a/plugins/check_nt.c b/plugins/check_nt.c
deleted file mode 100644
index 35ca92cd..00000000
--- a/plugins/check_nt.c
+++ /dev/null
@@ -1,789 +0,0 @@
1/*****************************************************************************
2 *
3 * Monitoring check_nt plugin
4 *
5 * License: GPL
6 * Copyright (c) 2000-2002 Yves Rubin (rubiyz@yahoo.com)
7 * Copyright (c) 2003-2024 Monitoring Plugins Development Team
8 *
9 * Description:
10 *
11 * This file contains the check_nt plugin
12 *
13 * This plugin collects data from the NSClient service running on a
14 * Windows NT/2000/XP/2003 server.
15 * This plugin requires NSClient software to run on NT
16 * (https://nsclient.org/)
17 *
18 *
19 * This program is free software: you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation, either version 3 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31 *
32 *
33 *****************************************************************************/
34
35const char *progname = "check_nt";
36const char *copyright = "2000-2024";
37const char *email = "devel@monitoring-plugins.org";
38
39#include "common.h"
40#include "netutils.h"
41#include "utils.h"
42#include "check_nt.d/config.h"
43
44enum {
45 MAX_VALUE_LIST = 30,
46};
47
48static char recv_buffer[MAX_INPUT_BUFFER];
49
50static void fetch_data(const char *address, int port, const char *sendb);
51
52typedef struct {
53 int errorcode;
54 check_nt_config config;
55} check_nt_config_wrapper;
56static check_nt_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
57
58static void preparelist(char *string);
59static bool strtoularray(unsigned long *array, char *string, const char *delim);
60static void print_help(void);
61void print_usage(void);
62
63int main(int argc, char **argv) {
64 setlocale(LC_ALL, "");
65 bindtextdomain(PACKAGE, LOCALEDIR);
66 textdomain(PACKAGE);
67
68 /* Parse extra opts if any */
69 argv = np_extra_opts(&argc, argv, progname);
70
71 check_nt_config_wrapper tmp_config = process_arguments(argc, argv);
72 if (tmp_config.errorcode == ERROR) {
73 usage4(_("Could not parse arguments"));
74 }
75
76 const check_nt_config config = tmp_config.config;
77
78 /* initialize alarm signal handling */
79 signal(SIGALRM, socket_timeout_alarm_handler);
80
81 /* set socket timeout */
82 alarm(socket_timeout);
83
84 int return_code = STATE_UNKNOWN;
85 char *send_buffer = NULL;
86 char *output_message = NULL;
87 char *perfdata = NULL;
88 char *temp_string = NULL;
89 char *temp_string_perf = NULL;
90 char *description = NULL;
91 char *counter_unit = NULL;
92 char *errcvt = NULL;
93 unsigned long lvalue_list[MAX_VALUE_LIST];
94 switch (config.vars_to_check) {
95 case CHECK_CLIENTVERSION:
96 xasprintf(&send_buffer, "%s&1", config.req_password);
97 fetch_data(config.server_address, config.server_port, send_buffer);
98 if (config.value_list != NULL && strcmp(recv_buffer, config.value_list) != 0) {
99 xasprintf(&output_message, _("Wrong client version - running: %s, required: %s"),
100 recv_buffer, config.value_list);
101 return_code = STATE_WARNING;
102 } else {
103 xasprintf(&output_message, "%s", recv_buffer);
104 return_code = STATE_OK;
105 }
106 break;
107 case CHECK_CPULOAD:
108 if (config.value_list == NULL) {
109 output_message = strdup(_("missing -l parameters"));
110 } else if (!strtoularray(lvalue_list, config.value_list, ",")) {
111 output_message = strdup(_("wrong -l parameter."));
112 } else {
113 /* -l parameters is present with only integers */
114 return_code = STATE_OK;
115 temp_string = strdup(_("CPU Load"));
116 temp_string_perf = strdup(" ");
117
118 /* loop until one of the parameters is wrong or not present */
119 int offset = 0;
120 while (lvalue_list[0 + offset] > (unsigned long)0 &&
121 lvalue_list[0 + offset] <= (unsigned long)17280 &&
122 lvalue_list[1 + offset] > (unsigned long)0 &&
123 lvalue_list[1 + offset] <= (unsigned long)100 &&
124 lvalue_list[2 + offset] > (unsigned long)0 &&
125 lvalue_list[2 + offset] <= (unsigned long)100) {
126
127 /* Send request and retrieve data */
128 xasprintf(&send_buffer, "%s&2&%lu", config.req_password, lvalue_list[0 + offset]);
129 fetch_data(config.server_address, config.server_port, send_buffer);
130
131 unsigned long utilization = strtoul(recv_buffer, NULL, 10);
132
133 /* Check if any of the request is in a warning or critical state */
134 if (utilization >= lvalue_list[2 + offset]) {
135 return_code = STATE_CRITICAL;
136 } else if (utilization >= lvalue_list[1 + offset] && return_code < STATE_WARNING) {
137 return_code = STATE_WARNING;
138 }
139
140 xasprintf(&output_message, _(" %lu%% (%lu min average)"), utilization,
141 lvalue_list[0 + offset]);
142 xasprintf(&temp_string, "%s%s", temp_string, output_message);
143 xasprintf(&perfdata, _(" '%lu min avg Load'=%lu%%;%lu;%lu;0;100"),
144 lvalue_list[0 + offset], utilization, lvalue_list[1 + offset],
145 lvalue_list[2 + offset]);
146 xasprintf(&temp_string_perf, "%s%s", temp_string_perf, perfdata);
147 offset += 3; /* move across the array */
148 }
149
150 if (strlen(temp_string) > 10) { /* we had at least one loop */
151 output_message = strdup(temp_string);
152 perfdata = temp_string_perf;
153 } else {
154 output_message = strdup(_("not enough values for -l parameters"));
155 }
156 }
157 break;
158 case CHECK_UPTIME: {
159 char *tmp_value_list = config.value_list;
160 if (config.value_list == NULL) {
161 tmp_value_list = "minutes";
162 }
163 if (strncmp(tmp_value_list, "seconds", strlen("seconds") + 1) &&
164 strncmp(tmp_value_list, "minutes", strlen("minutes") + 1) &&
165 strncmp(config.value_list, "hours", strlen("hours") + 1) &&
166 strncmp(tmp_value_list, "days", strlen("days") + 1)) {
167
168 output_message = strdup(_("wrong -l argument"));
169 } else {
170 xasprintf(&send_buffer, "%s&3", config.req_password);
171 fetch_data(config.server_address, config.server_port, send_buffer);
172 unsigned long uptime = strtoul(recv_buffer, NULL, 10);
173 int updays = uptime / 86400;
174 int uphours = (uptime % 86400) / 3600;
175 int upminutes = ((uptime % 86400) % 3600) / 60;
176
177 if (!strncmp(tmp_value_list, "minutes", strlen("minutes"))) {
178 uptime = uptime / 60;
179 } else if (!strncmp(tmp_value_list, "hours", strlen("hours"))) {
180 uptime = uptime / 3600;
181 } else if (!strncmp(tmp_value_list, "days", strlen("days"))) {
182 uptime = uptime / 86400;
183 }
184 /* else uptime in seconds, nothing to do */
185
186 xasprintf(&output_message,
187 _("System Uptime - %u day(s) %u hour(s) %u minute(s) |uptime=%lu"), updays,
188 uphours, upminutes, uptime);
189
190 if (config.check_critical_value && uptime <= config.critical_value) {
191 return_code = STATE_CRITICAL;
192 } else if (config.check_warning_value && uptime <= config.warning_value) {
193 return_code = STATE_WARNING;
194 } else {
195 return_code = STATE_OK;
196 }
197 }
198 } break;
199 case CHECK_USEDDISKSPACE:
200 if (config.value_list == NULL) {
201 output_message = strdup(_("missing -l parameters"));
202 } else if (strlen(config.value_list) != 1) {
203 output_message = strdup(_("wrong -l argument"));
204 } else {
205 xasprintf(&send_buffer, "%s&4&%s", config.req_password, config.value_list);
206 fetch_data(config.server_address, config.server_port, send_buffer);
207 char *fds = strtok(recv_buffer, "&");
208 char *tds = strtok(NULL, "&");
209 double total_disk_space = 0;
210 double free_disk_space = 0;
211 if (fds != NULL) {
212 free_disk_space = atof(fds);
213 }
214 if (tds != NULL) {
215 total_disk_space = atof(tds);
216 }
217
218 if (total_disk_space > 0 && free_disk_space >= 0) {
219 double percent_used_space =
220 ((total_disk_space - free_disk_space) / total_disk_space) * 100;
221 double warning_used_space = ((float)config.warning_value / 100) * total_disk_space;
222 double critical_used_space =
223 ((float)config.critical_value / 100) * total_disk_space;
224
225 xasprintf(
226 &temp_string,
227 _("%s:\\ - total: %.2f Gb - used: %.2f Gb (%.0f%%) - free %.2f Gb (%.0f%%)"),
228 config.value_list, total_disk_space / 1073741824,
229 (total_disk_space - free_disk_space) / 1073741824, percent_used_space,
230 free_disk_space / 1073741824, (free_disk_space / total_disk_space) * 100);
231 xasprintf(&temp_string_perf, _("'%s:\\ Used Space'=%.2fGb;%.2f;%.2f;0.00;%.2f"),
232 config.value_list, (total_disk_space - free_disk_space) / 1073741824,
233 warning_used_space / 1073741824, critical_used_space / 1073741824,
234 total_disk_space / 1073741824);
235
236 if (config.check_critical_value && percent_used_space >= config.critical_value) {
237 return_code = STATE_CRITICAL;
238 } else if (config.check_warning_value &&
239 percent_used_space >= config.warning_value) {
240 return_code = STATE_WARNING;
241 } else {
242 return_code = STATE_OK;
243 }
244
245 output_message = strdup(temp_string);
246 perfdata = temp_string_perf;
247 } else {
248 output_message = strdup(_("Free disk space : Invalid drive"));
249 return_code = STATE_UNKNOWN;
250 }
251 }
252 break;
253 case CHECK_SERVICESTATE:
254 case CHECK_PROCSTATE:
255 if (config.value_list == NULL) {
256 output_message = strdup(_("No service/process specified"));
257 } else {
258 preparelist(
259 config.value_list); /* replace , between services with & to send the request */
260 xasprintf(&send_buffer, "%s&%u&%s&%s", config.req_password,
261 (config.vars_to_check == CHECK_SERVICESTATE) ? 5 : 6,
262 (config.show_all) ? "ShowAll" : "ShowFail", config.value_list);
263 fetch_data(config.server_address, config.server_port, send_buffer);
264 char *numstr = strtok(recv_buffer, "&");
265 if (numstr == NULL) {
266 die(STATE_UNKNOWN, _("could not fetch information from server\n"));
267 }
268 return_code = atoi(numstr);
269 temp_string = strtok(NULL, "&");
270 output_message = strdup(temp_string);
271 }
272 break;
273 case CHECK_MEMUSE:
274 xasprintf(&send_buffer, "%s&7", config.req_password);
275 fetch_data(config.server_address, config.server_port, send_buffer);
276 char *numstr = strtok(recv_buffer, "&");
277 if (numstr == NULL) {
278 die(STATE_UNKNOWN, _("could not fetch information from server\n"));
279 }
280 double mem_commitLimit = atof(numstr);
281 numstr = strtok(NULL, "&");
282 if (numstr == NULL) {
283 die(STATE_UNKNOWN, _("could not fetch information from server\n"));
284 }
285 double mem_commitByte = atof(numstr);
286 double percent_used_space = (mem_commitByte / mem_commitLimit) * 100;
287 double warning_used_space = ((float)config.warning_value / 100) * mem_commitLimit;
288 double critical_used_space = ((float)config.critical_value / 100) * mem_commitLimit;
289
290 /* Divisor should be 1048567, not 3044515, as we are measuring "Commit Charge" here,
291 which equals RAM + Pagefiles. */
292 xasprintf(
293 &output_message,
294 _("Memory usage: total:%.2f MB - used: %.2f MB (%.0f%%) - free: %.2f MB (%.0f%%)"),
295 mem_commitLimit / 1048567, mem_commitByte / 1048567, percent_used_space,
296 (mem_commitLimit - mem_commitByte) / 1048567,
297 (mem_commitLimit - mem_commitByte) / mem_commitLimit * 100);
298 xasprintf(&perfdata, _("'Memory usage'=%.2fMB;%.2f;%.2f;0.00;%.2f"),
299 mem_commitByte / 1048567, warning_used_space / 1048567,
300 critical_used_space / 1048567, mem_commitLimit / 1048567);
301
302 return_code = STATE_OK;
303 if (config.check_critical_value && percent_used_space >= config.critical_value) {
304 return_code = STATE_CRITICAL;
305 } else if (config.check_warning_value && percent_used_space >= config.warning_value) {
306 return_code = STATE_WARNING;
307 }
308
309 break;
310 case CHECK_COUNTER: {
311 /*
312 CHECK_COUNTER has been modified to provide extensive perfdata information.
313 In order to do this, some modifications have been done to the code
314 and some constraints have been introduced.
315
316 1) For the sake of simplicity of the code, perfdata information will only be
317 provided when the "description" field is added.
318
319 2) If the counter you're going to measure is percent-based, the code will detect
320 the percent sign in its name and will attribute minimum (0%) and maximum (100%)
321 values automagically, as well the "%" sign to graph units.
322
323 3) OTOH, if the counter is "absolute", you'll have to provide the following
324 the counter unit - that is, the dimensions of the counter you're getting. Examples:
325 pages/s, packets transferred, etc.
326
327 4) If you want, you may provide the minimum and maximum values to expect. They aren't
328 mandatory, but once specified they MUST have the same order of magnitude and units of -w and
329 -c; otherwise. strange things will happen when you make graphs of your data.
330 */
331
332 double counter_value = 0.0;
333 if (config.value_list == NULL) {
334 output_message = strdup(_("No counter specified"));
335 } else {
336 preparelist(
337 config.value_list); /* replace , between services with & to send the request */
338 bool isPercent = (strchr(config.value_list, '%') != NULL);
339
340 strtok(config.value_list, "&"); /* burn the first parameters */
341 description = strtok(NULL, "&");
342 counter_unit = strtok(NULL, "&");
343 xasprintf(&send_buffer, "%s&8&%s", config.req_password, config.value_list);
344 fetch_data(config.server_address, config.server_port, send_buffer);
345 counter_value = atof(recv_buffer);
346
347 bool allRight = false;
348 if (description == NULL) {
349 xasprintf(&output_message, "%.f", counter_value);
350 } else if (isPercent) {
351 counter_unit = strdup("%");
352 allRight = true;
353 }
354
355 char *minval = NULL;
356 char *maxval = NULL;
357 double fminval = 0;
358 double fmaxval = 0;
359 if ((counter_unit != NULL) && (!allRight)) {
360 minval = strtok(NULL, "&");
361 maxval = strtok(NULL, "&");
362
363 /* All parameters specified. Let's check the numbers */
364
365 fminval = (minval != NULL) ? strtod(minval, &errcvt) : -1;
366 fmaxval = (minval != NULL) ? strtod(maxval, &errcvt) : -1;
367
368 if ((fminval == 0) && (minval == errcvt)) {
369 output_message = strdup(_("Minimum value contains non-numbers"));
370 } else {
371 if ((fmaxval == 0) && (maxval == errcvt)) {
372 output_message = strdup(_("Maximum value contains non-numbers"));
373 } else {
374 allRight = true; /* Everything is OK. */
375 }
376 }
377 } else if ((counter_unit == NULL) && (description != NULL)) {
378 output_message = strdup(_("No unit counter specified"));
379 }
380
381 if (allRight) {
382 /* Let's format the output string, finally... */
383 if (strstr(description, "%") == NULL) {
384 xasprintf(&output_message, "%s = %.2f %s", description, counter_value,
385 counter_unit);
386 } else {
387 /* has formatting, will segv if wrong */
388 xasprintf(&output_message, description, counter_value);
389 }
390 xasprintf(&output_message, "%s |", output_message);
391 xasprintf(&output_message, "%s %s", output_message,
392 fperfdata(description, counter_value, counter_unit, 1,
393 config.warning_value, 1, config.critical_value,
394 (!(isPercent) && (minval != NULL)), fminval,
395 (!(isPercent) && (minval != NULL)), fmaxval));
396 }
397 }
398
399 if (config.critical_value > config.warning_value) { /* Normal thresholds */
400 if (config.check_critical_value && counter_value >= config.critical_value) {
401 return_code = STATE_CRITICAL;
402 } else if (config.check_warning_value && counter_value >= config.warning_value) {
403 return_code = STATE_WARNING;
404 } else {
405 return_code = STATE_OK;
406 }
407 } else { /* inverse thresholds */
408 return_code = STATE_OK;
409 if (config.check_critical_value && counter_value <= config.critical_value) {
410 return_code = STATE_CRITICAL;
411 } else if (config.check_warning_value && counter_value <= config.warning_value) {
412 return_code = STATE_WARNING;
413 }
414 }
415 } break;
416 case CHECK_FILEAGE:
417 if (config.value_list == NULL) {
418 output_message = strdup(_("No counter specified"));
419 } else {
420 preparelist(
421 config.value_list); /* replace , between services with & to send the request */
422 xasprintf(&send_buffer, "%s&9&%s", config.req_password, config.value_list);
423 fetch_data(config.server_address, config.server_port, send_buffer);
424 unsigned long age_in_minutes = atoi(strtok(recv_buffer, "&"));
425 description = strtok(NULL, "&");
426 output_message = strdup(description);
427
428 if (config.critical_value > config.warning_value) { /* Normal thresholds */
429 if (config.check_critical_value && age_in_minutes >= config.critical_value) {
430 return_code = STATE_CRITICAL;
431 } else if (config.check_warning_value && age_in_minutes >= config.warning_value) {
432 return_code = STATE_WARNING;
433 } else {
434 return_code = STATE_OK;
435 }
436 } else { /* inverse thresholds */
437 if (config.check_critical_value && age_in_minutes <= config.critical_value) {
438 return_code = STATE_CRITICAL;
439 } else if (config.check_warning_value && age_in_minutes <= config.warning_value) {
440 return_code = STATE_WARNING;
441 } else {
442 return_code = STATE_OK;
443 }
444 }
445 }
446 break;
447
448 case CHECK_INSTANCES:
449 if (config.value_list == NULL) {
450 output_message = strdup(_("No counter specified"));
451 } else {
452 xasprintf(&send_buffer, "%s&10&%s", config.req_password, config.value_list);
453 fetch_data(config.server_address, config.server_port, send_buffer);
454 if (!strncmp(recv_buffer, "ERROR", 5)) {
455 printf("NSClient - %s\n", recv_buffer);
456 exit(STATE_UNKNOWN);
457 }
458 xasprintf(&output_message, "%s", recv_buffer);
459 return_code = STATE_OK;
460 }
461 break;
462
463 case CHECK_NONE:
464 default:
465 usage4(_("Please specify a variable to check"));
466 break;
467 }
468
469 /* reset timeout */
470 alarm(0);
471
472 if (perfdata == NULL) {
473 printf("%s\n", output_message);
474 } else {
475 printf("%s | %s\n", output_message, perfdata);
476 }
477 return return_code;
478}
479
480/* process command-line arguments */
481check_nt_config_wrapper process_arguments(int argc, char **argv) {
482 static struct option longopts[] = {{"port", required_argument, 0, 'p'},
483 {"timeout", required_argument, 0, 't'},
484 {"critical", required_argument, 0, 'c'},
485 {"warning", required_argument, 0, 'w'},
486 {"variable", required_argument, 0, 'v'},
487 {"hostname", required_argument, 0, 'H'},
488 {"params", required_argument, 0, 'l'},
489 {"secret", required_argument, 0, 's'},
490 {"display", required_argument, 0, 'd'},
491 {"unknown-timeout", no_argument, 0, 'u'},
492 {"version", no_argument, 0, 'V'},
493 {"help", no_argument, 0, 'h'},
494 {0, 0, 0, 0}};
495
496 check_nt_config_wrapper result = {
497 .errorcode = OK,
498 .config = check_nt_config_init(),
499 };
500
501 /* no options were supplied */
502 if (argc < 2) {
503 result.errorcode = ERROR;
504 return result;
505 }
506
507 /* backwards compatibility */
508 if (!is_option(argv[1])) {
509 result.config.server_address = strdup(argv[1]);
510 argv[1] = argv[0];
511 argv = &argv[1];
512 argc--;
513 }
514
515 for (int index = 1; index < argc; index++) {
516 if (strcmp("-to", argv[index]) == 0) {
517 strcpy(argv[index], "-t");
518 } else if (strcmp("-wv", argv[index]) == 0) {
519 strcpy(argv[index], "-w");
520 } else if (strcmp("-cv", argv[index]) == 0) {
521 strcpy(argv[index], "-c");
522 }
523 }
524
525 int option = 0;
526 while (true) {
527 int option_index = getopt_long(argc, argv, "+hVH:t:c:w:p:v:l:s:d:u", longopts, &option);
528
529 if (option_index == -1 || option_index == EOF || option_index == 1) {
530 break;
531 }
532
533 switch (option_index) {
534 case '?': /* print short usage statement if args not parsable */
535 usage5();
536 case 'h': /* help */
537 print_help();
538 exit(STATE_UNKNOWN);
539 case 'V': /* version */
540 print_revision(progname, NP_VERSION);
541 exit(STATE_UNKNOWN);
542 case 'H': /* hostname */
543 result.config.server_address = optarg;
544 break;
545 case 's': /* password */
546 result.config.req_password = optarg;
547 break;
548 case 'p': /* port */
549 if (is_intnonneg(optarg)) {
550 result.config.server_port = atoi(optarg);
551 } else {
552 die(STATE_UNKNOWN, _("Server port must be an integer\n"));
553 }
554 break;
555 case 'v':
556 if (strlen(optarg) < 4) {
557 result.errorcode = ERROR;
558 return result;
559 }
560 if (!strcmp(optarg, "CLIENTVERSION")) {
561 result.config.vars_to_check = CHECK_CLIENTVERSION;
562 } else if (!strcmp(optarg, "CPULOAD")) {
563 result.config.vars_to_check = CHECK_CPULOAD;
564 } else if (!strcmp(optarg, "UPTIME")) {
565 result.config.vars_to_check = CHECK_UPTIME;
566 } else if (!strcmp(optarg, "USEDDISKSPACE")) {
567 result.config.vars_to_check = CHECK_USEDDISKSPACE;
568 } else if (!strcmp(optarg, "SERVICESTATE")) {
569 result.config.vars_to_check = CHECK_SERVICESTATE;
570 } else if (!strcmp(optarg, "PROCSTATE")) {
571 result.config.vars_to_check = CHECK_PROCSTATE;
572 } else if (!strcmp(optarg, "MEMUSE")) {
573 result.config.vars_to_check = CHECK_MEMUSE;
574 } else if (!strcmp(optarg, "COUNTER")) {
575 result.config.vars_to_check = CHECK_COUNTER;
576 } else if (!strcmp(optarg, "FILEAGE")) {
577 result.config.vars_to_check = CHECK_FILEAGE;
578 } else if (!strcmp(optarg, "INSTANCES")) {
579 result.config.vars_to_check = CHECK_INSTANCES;
580 } else {
581 result.errorcode = ERROR;
582 return result;
583 }
584 break;
585 case 'l': /* value list */
586 result.config.value_list = optarg;
587 break;
588 case 'w': /* warning threshold */
589 result.config.warning_value = strtoul(optarg, NULL, 10);
590 result.config.check_warning_value = true;
591 break;
592 case 'c': /* critical threshold */
593 result.config.critical_value = strtoul(optarg, NULL, 10);
594 result.config.check_critical_value = true;
595 break;
596 case 'd': /* Display select for services */
597 if (!strcmp(optarg, "SHOWALL")) {
598 result.config.show_all = true;
599 }
600 break;
601 case 'u':
602 socket_timeout_state = STATE_UNKNOWN;
603 break;
604 case 't': /* timeout */
605 socket_timeout = atoi(optarg);
606 if (socket_timeout <= 0) {
607 result.errorcode = ERROR;
608 return result;
609 }
610 }
611 }
612 if (result.config.server_address == NULL) {
613 usage4(_("You must provide a server address or host name"));
614 }
615
616 if (result.config.vars_to_check == CHECK_NONE) {
617 result.errorcode = ERROR;
618 return result;
619 }
620
621 if (result.config.req_password == NULL) {
622 result.config.req_password = strdup(_("None"));
623 }
624
625 return result;
626}
627
628void fetch_data(const char *address, int port, const char *sendb) {
629 int result = process_tcp_request(address, port, sendb, recv_buffer, sizeof(recv_buffer));
630
631 if (result != STATE_OK) {
632 die(result, _("could not fetch information from server\n"));
633 }
634
635 if (!strncmp(recv_buffer, "ERROR", 5)) {
636 die(STATE_UNKNOWN, "NSClient - %s\n", recv_buffer);
637 }
638}
639
640bool strtoularray(unsigned long *array, char *string, const char *delim) {
641 /* split a <delim> delimited string into a long array */
642 for (int idx = 0; idx < MAX_VALUE_LIST; idx++) {
643 array[idx] = 0;
644 }
645
646 int idx = 0;
647 for (char *t1 = strtok(string, delim); t1 != NULL; t1 = strtok(NULL, delim)) {
648 if (is_numeric(t1) && idx < MAX_VALUE_LIST) {
649 array[idx] = strtoul(t1, NULL, 10);
650 idx++;
651 } else {
652 return false;
653 }
654 }
655 return true;
656}
657
658void preparelist(char *string) {
659 /* Replace all , with & which is the delimiter for the request */
660 for (int i = 0; (size_t)i < strlen(string); i++) {
661 if (string[i] == ',') {
662 string[i] = '&';
663 }
664 }
665}
666
667void print_help(void) {
668 print_revision(progname, NP_VERSION);
669
670 printf("Copyright (c) 2000 Yves Rubin (rubiyz@yahoo.com)\n");
671 printf(COPYRIGHT, copyright, email);
672
673 printf("%s\n", _("This plugin collects data from the NSClient service running on a"));
674 printf("%s\n", _("Windows NT/2000/XP/2003 server."));
675
676 printf("\n\n");
677
678 print_usage();
679
680 printf(UT_HELP_VRSN);
681 printf(UT_EXTRA_OPTS);
682
683 printf("%s\n", _("Options:"));
684 printf(" %s\n", "-H, --hostname=HOST");
685 printf(" %s\n", _("Name of the host to check"));
686 printf(" %s\n", "-p, --port=INTEGER");
687 printf(" %s", _("Optional port number (default: "));
688 printf("%d)\n", PORT);
689 printf(" %s\n", "-s, --secret=<password>");
690 printf(" %s\n", _("Password needed for the request"));
691 printf(" %s\n", "-w, --warning=INTEGER");
692 printf(" %s\n", _("Threshold which will result in a warning status"));
693 printf(" %s\n", "-c, --critical=INTEGER");
694 printf(" %s\n", _("Threshold which will result in a critical status"));
695 printf(" %s\n", "-t, --timeout=INTEGER");
696 printf(" %s", _("Seconds before connection attempt times out (default: "));
697 printf(" %s\n", "-l, --params=<parameters>");
698 printf(" %s", _("Parameters passed to specified check (see below)"));
699 printf(" %s\n", "-d, --display={SHOWALL}");
700 printf(" %s", _("Display options (currently only SHOWALL works)"));
701 printf(" %s\n", "-u, --unknown-timeout");
702 printf(" %s", _("Return UNKNOWN on timeouts"));
703 printf("%d)\n", DEFAULT_SOCKET_TIMEOUT);
704 printf(" %s\n", "-h, --help");
705 printf(" %s\n", _("Print this help screen"));
706 printf(" %s\n", "-V, --version");
707 printf(" %s\n", _("Print version information"));
708 printf(" %s\n", "-v, --variable=STRING");
709 printf(" %s\n\n", _("Variable to check"));
710 printf("%s\n", _("Valid variables are:"));
711 printf(" %s", "CLIENTVERSION =");
712 printf(" %s\n", _("Get the NSClient version"));
713 printf(" %s\n", _("If -l <version> is specified, will return warning if versions differ."));
714 printf(" %s\n", "CPULOAD =");
715 printf(" %s\n", _("Average CPU load on last x minutes."));
716 printf(" %s\n", _("Request a -l parameter with the following syntax:"));
717 printf(" %s\n", _("-l <minutes range>,<warning threshold>,<critical threshold>."));
718 printf(" %s\n", _("<minute range> should be less than 24*60."));
719 printf(" %s\n", _("Thresholds are percentage and up to 10 requests can be done in one shot."));
720 printf(" %s\n", "ie: -l 60,90,95,120,90,95");
721 printf(" %s\n", "UPTIME =");
722 printf(" %s\n", _("Get the uptime of the machine."));
723 printf(" %s\n", _("-l <unit> "));
724 printf(" %s\n", _("<unit> = seconds, minutes, hours, or days. (default: minutes)"));
725 printf(" %s\n", _("Thresholds will use the unit specified above."));
726 printf(" %s\n", "USEDDISKSPACE =");
727 printf(" %s\n", _("Size and percentage of disk use."));
728 printf(" %s\n", _("Request a -l parameter containing the drive letter only."));
729 printf(" %s\n", _("Warning and critical thresholds can be specified with -w and -c."));
730 printf(" %s\n", "MEMUSE =");
731 printf(" %s\n", _("Memory use."));
732 printf(" %s\n", _("Warning and critical thresholds can be specified with -w and -c."));
733 printf(" %s\n", "SERVICESTATE =");
734 printf(" %s\n", _("Check the state of one or several services."));
735 printf(" %s\n", _("Request a -l parameters with the following syntax:"));
736 printf(" %s\n", _("-l <service1>,<service2>,<service3>,..."));
737 printf(" %s\n", _("You can specify -d SHOWALL in case you want to see working services"));
738 printf(" %s\n", _("in the returned string."));
739 printf(" %s\n", "PROCSTATE =");
740 printf(" %s\n", _("Check if one or several process are running."));
741 printf(" %s\n", _("Same syntax as SERVICESTATE."));
742 printf(" %s\n", "COUNTER =");
743 printf(" %s\n", _("Check any performance counter of Windows NT/2000."));
744 printf(" %s\n", _("Request a -l parameters with the following syntax:"));
745 printf(" %s\n", _("-l \"\\\\<performance object>\\\\counter\",\"<description>"));
746 printf(" %s\n", _("The <description> parameter is optional and is given to a printf "));
747 printf(" %s\n", _("output command which requires a float parameter."));
748 printf(" %s\n", _("If <description> does not include \"%%\", it is used as a label."));
749 printf(" %s\n", _("Some examples:"));
750 printf(" %s\n", "\"Paging file usage is %%.2f %%%%\"");
751 printf(" %s\n", "\"%%.f %%%% paging file used.\"");
752 printf(" %s\n", "INSTANCES =");
753 printf(" %s\n", _("Check any performance counter object of Windows NT/2000."));
754 printf(" %s\n",
755 _("Syntax: check_nt -H <hostname> -p <port> -v INSTANCES -l <counter object>"));
756 printf(" %s\n", _("<counter object> is a Windows Perfmon Counter object (eg. Process),"));
757 printf(" %s\n", _("if it is two words, it should be enclosed in quotes"));
758 printf(" %s\n", _("The returned results will be a comma-separated list of instances on "));
759 printf(" %s\n", _(" the selected computer for that object."));
760 printf(" %s\n",
761 _("The purpose of this is to be run from command line to determine what instances"));
762 printf(" %s\n",
763 _(" are available for monitoring without having to log onto the Windows server"));
764 printf(" %s\n", _(" to run Perfmon directly."));
765 printf(" %s\n",
766 _("It can also be used in scripts that automatically create the monitoring service"));
767 printf(" %s\n", _(" configuration files."));
768 printf(" %s\n", _("Some examples:"));
769 printf(" %s\n\n", _("check_nt -H 192.168.1.1 -p 1248 -v INSTANCES -l Process"));
770
771 printf("%s\n", _("Notes:"));
772 printf(" %s\n",
773 _("- The NSClient service should be running on the server to get any information"));
774 printf(" %s\n", "(http://nsclient.ready2run.nl).");
775 printf(" %s\n", _("- Critical thresholds should be lower than warning thresholds"));
776 printf(" %s\n", _("- Default port 1248 is sometimes in use by other services. The error"));
777 printf(" %s\n",
778 _("output when this happens contains \"Cannot map xxxxx to protocol number\"."));
779 printf(" %s\n", _("One fix for this is to change the port to something else on check_nt "));
780 printf(" %s\n", _("and on the client service it\'s connecting to."));
781
782 printf(UT_SUPPORT);
783}
784
785void print_usage(void) {
786 printf("%s\n", _("Usage:"));
787 printf("%s -H host -v variable [-p port] [-w warning] [-c critical]\n", progname);
788 printf("[-l params] [-d SHOWALL] [-u] [-t timeout]\n");
789}
diff --git a/plugins/check_nt.d/config.h b/plugins/check_nt.d/config.h
deleted file mode 100644
index 431889cb..00000000
--- a/plugins/check_nt.d/config.h
+++ /dev/null
@@ -1,53 +0,0 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6enum {
7 PORT = 1248,
8};
9
10enum checkvars {
11 CHECK_NONE,
12 CHECK_CLIENTVERSION,
13 CHECK_CPULOAD,
14 CHECK_UPTIME,
15 CHECK_USEDDISKSPACE,
16 CHECK_SERVICESTATE,
17 CHECK_PROCSTATE,
18 CHECK_MEMUSE,
19 CHECK_COUNTER,
20 CHECK_FILEAGE,
21 CHECK_INSTANCES
22};
23
24typedef struct {
25 char *server_address;
26 int server_port;
27 char *req_password;
28 enum checkvars vars_to_check;
29 bool show_all;
30 char *value_list;
31 bool check_warning_value;
32 unsigned long warning_value;
33 bool check_critical_value;
34 unsigned long critical_value;
35} check_nt_config;
36
37check_nt_config check_nt_config_init() {
38 check_nt_config tmp = {
39 .server_address = NULL,
40 .server_port = PORT,
41 .req_password = NULL,
42
43 .vars_to_check = CHECK_NONE,
44 .show_all = false,
45 .value_list = NULL,
46
47 .check_warning_value = false,
48 .warning_value = 0,
49 .check_critical_value = false,
50 .critical_value = 0,
51 };
52 return tmp;
53}
diff --git a/plugins/check_ntp.c b/plugins/check_ntp.c
deleted file mode 100644
index b22cc3c1..00000000
--- a/plugins/check_ntp.c
+++ /dev/null
@@ -1,947 +0,0 @@
1/*****************************************************************************
2 *
3 * Monitoring check_ntp plugin
4 *
5 * License: GPL
6 * Copyright (c) 2006 Sean Finney <seanius@seanius.net>
7 * Copyright (c) 2006-2024 Monitoring Plugins Development Team
8 *
9 * Description:
10 *
11 * This file contains the check_ntp plugin
12 *
13 * This plugin to check ntp servers independent of any commandline
14 * programs or external libraries.
15 *
16 *
17 * This program is free software: you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation, either version 3 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29 *
30 *
31 *****************************************************************************/
32
33const char *progname = "check_ntp";
34const char *copyright = "2006-2024";
35const char *email = "devel@monitoring-plugins.org";
36
37#include "common.h"
38#include "netutils.h"
39#include "utils.h"
40
41static char *server_address = NULL;
42static int verbose = 0;
43static bool do_offset = false;
44static char *owarn = "60";
45static char *ocrit = "120";
46static bool do_jitter = false;
47static char *jwarn = "5000";
48static char *jcrit = "10000";
49
50static int process_arguments(int /*argc*/, char ** /*argv*/);
51static thresholds *offset_thresholds = NULL;
52static thresholds *jitter_thresholds = NULL;
53static void print_help(void);
54void print_usage(void);
55
56/* number of times to perform each request to get a good average. */
57#ifndef AVG_NUM
58# define AVG_NUM 4
59#endif
60
61/* max size of control message data */
62#define MAX_CM_SIZE 468
63
64/* this structure holds everything in an ntp request/response as per rfc1305 */
65typedef struct {
66 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
67 uint8_t stratum; /* clock stratum */
68 int8_t poll; /* polling interval */
69 int8_t precision; /* precision of the local clock */
70 int32_t rtdelay; /* total rt delay, as a fixed point num. see macros */
71 uint32_t rtdisp; /* like above, but for max err to primary src */
72 uint32_t refid; /* ref clock identifier */
73 uint64_t refts; /* reference timestamp. local time local clock */
74 uint64_t origts; /* time at which request departed client */
75 uint64_t rxts; /* time at which request arrived at server */
76 uint64_t txts; /* time at which request departed server */
77} ntp_message;
78
79/* this structure holds data about results from querying offset from a peer */
80typedef struct {
81 time_t waiting; /* ts set when we started waiting for a response */
82 int num_responses; /* number of successfully received responses */
83 uint8_t stratum; /* copied verbatim from the ntp_message */
84 double rtdelay; /* converted from the ntp_message */
85 double rtdisp; /* converted from the ntp_message */
86 double offset[AVG_NUM]; /* offsets from each response */
87 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
88} ntp_server_results;
89
90/* this structure holds everything in an ntp control message as per rfc1305 */
91typedef struct {
92 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
93 uint8_t op; /* R,E,M bits and Opcode */
94 uint16_t seq; /* Packet sequence */
95 uint16_t status; /* Clock status */
96 uint16_t assoc; /* Association */
97 uint16_t offset; /* Similar to TCP sequence # */
98 uint16_t count; /* # bytes of data */
99 char data[MAX_CM_SIZE]; /* ASCII data of the request */
100 /* NB: not necessarily NULL terminated! */
101} ntp_control_message;
102
103/* this is an association/status-word pair found in control packet responses */
104typedef struct {
105 uint16_t assoc;
106 uint16_t status;
107} ntp_assoc_status_pair;
108
109/* bits 1,2 are the leap indicator */
110#define LI_MASK 0xc0
111#define LI(x) ((x & LI_MASK) >> 6)
112#define LI_SET(x, y) \
113 do { \
114 x |= ((y << 6) & LI_MASK); \
115 } while (0)
116/* and these are the values of the leap indicator */
117#define LI_NOWARNING 0x00
118#define LI_EXTRASEC 0x01
119#define LI_MISSINGSEC 0x02
120#define LI_ALARM 0x03
121/* bits 3,4,5 are the ntp version */
122#define VN_MASK 0x38
123#define VN(x) ((x & VN_MASK) >> 3)
124#define VN_SET(x, y) \
125 do { \
126 x |= ((y << 3) & VN_MASK); \
127 } while (0)
128#define VN_RESERVED 0x02
129/* bits 6,7,8 are the ntp mode */
130#define MODE_MASK 0x07
131#define MODE(x) (x & MODE_MASK)
132#define MODE_SET(x, y) \
133 do { \
134 x |= (y & MODE_MASK); \
135 } while (0)
136/* here are some values */
137#define MODE_CLIENT 0x03
138#define MODE_CONTROLMSG 0x06
139/* In control message, bits 8-10 are R,E,M bits */
140#define REM_MASK 0xe0
141#define REM_RESP 0x80
142#define REM_ERROR 0x40
143#define REM_MORE 0x20
144/* In control message, bits 11 - 15 are opcode */
145#define OP_MASK 0x1f
146#define OP_SET(x, y) \
147 do { \
148 x |= (y & OP_MASK); \
149 } while (0)
150#define OP_READSTAT 0x01
151#define OP_READVAR 0x02
152/* In peer status bytes, bits 6,7,8 determine clock selection status */
153#define PEER_SEL(x) ((ntohs(x) >> 8) & 0x07)
154#define PEER_INCLUDED 0x04
155#define PEER_SYNCSOURCE 0x06
156
157/**
158 ** a note about the 32-bit "fixed point" numbers:
159 **
160 they are divided into halves, each being a 16-bit int in network byte order:
161 - the first 16 bits are an int on the left side of a decimal point.
162 - the second 16 bits represent a fraction n/(2^16)
163 likewise for the 64-bit "fixed point" numbers with everything doubled :)
164 **/
165
166/* macros to access the left/right 16 bits of a 32-bit ntp "fixed point"
167 number. note that these can be used as lvalues too */
168#define L16(x) (((uint16_t *)&x)[0])
169#define R16(x) (((uint16_t *)&x)[1])
170/* macros to access the left/right 32 bits of a 64-bit ntp "fixed point"
171 number. these too can be used as lvalues */
172#define L32(x) (((uint32_t *)&x)[0])
173#define R32(x) (((uint32_t *)&x)[1])
174
175/* ntp wants seconds since 1/1/00, epoch is 1/1/70. this is the difference */
176#define EPOCHDIFF 0x83aa7e80UL
177
178/* extract a 32-bit ntp fixed point number into a double */
179#define NTP32asDOUBLE(x) (ntohs(L16(x)) + (double)ntohs(R16(x)) / 65536.0)
180
181/* likewise for a 64-bit ntp fp number */
182#define NTP64asDOUBLE(n) \
183 (double)(((uint64_t)n) ? (ntohl(L32(n)) - EPOCHDIFF) + \
184 (.00000001 * (0.5 + (double)(ntohl(R32(n)) / 42.94967296))) \
185 : 0)
186
187/* convert a struct timeval to a double */
188#define TVasDOUBLE(x) (double)(x.tv_sec + (0.000001 * x.tv_usec))
189
190/* convert an ntp 64-bit fp number to a struct timeval */
191#define NTP64toTV(n, t) \
192 do { \
193 if (!n) \
194 t.tv_sec = t.tv_usec = 0; \
195 else { \
196 t.tv_sec = ntohl(L32(n)) - EPOCHDIFF; \
197 t.tv_usec = (int)(0.5 + (double)(ntohl(R32(n)) / 4294.967296)); \
198 } \
199 } while (0)
200
201/* convert a struct timeval to an ntp 64-bit fp number */
202#define TVtoNTP64(t, n) \
203 do { \
204 if (!t.tv_usec && !t.tv_sec) \
205 n = 0x0UL; \
206 else { \
207 L32(n) = htonl(t.tv_sec + EPOCHDIFF); \
208 R32(n) = htonl((uint64_t)((4294.967296 * t.tv_usec) + .5)); \
209 } \
210 } while (0)
211
212/* NTP control message header is 12 bytes, plus any data in the data
213 * field, plus null padding to the nearest 32-bit boundary per rfc.
214 */
215#define SIZEOF_NTPCM(m) \
216 (12 + ntohs(m.count) + ((ntohs(m.count) % 4) ? 4 - (ntohs(m.count) % 4) : 0))
217
218/* finally, a little helper or two for debugging: */
219#define DBG(x) \
220 do { \
221 if (verbose > 1) { \
222 x; \
223 } \
224 } while (0);
225#define PRINTSOCKADDR(x) \
226 do { \
227 printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \
228 } while (0);
229
230/* calculate the offset of the local clock */
231static inline double calc_offset(const ntp_message *m, const struct timeval *t) {
232 double client_tx, peer_rx, peer_tx, client_rx;
233 client_tx = NTP64asDOUBLE(m->origts);
234 peer_rx = NTP64asDOUBLE(m->rxts);
235 peer_tx = NTP64asDOUBLE(m->txts);
236 client_rx = TVasDOUBLE((*t));
237 return (.5 * ((peer_tx - client_rx) + (peer_rx - client_tx)));
238}
239
240/* print out a ntp packet in human readable/debuggable format */
241void print_ntp_message(const ntp_message *p) {
242 struct timeval ref, orig, rx, tx;
243
244 NTP64toTV(p->refts, ref);
245 NTP64toTV(p->origts, orig);
246 NTP64toTV(p->rxts, rx);
247 NTP64toTV(p->txts, tx);
248
249 printf("packet contents:\n");
250 printf("\tflags: 0x%.2x\n", p->flags);
251 printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags & LI_MASK);
252 printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags & VN_MASK);
253 printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags & MODE_MASK);
254 printf("\tstratum = %d\n", p->stratum);
255 printf("\tpoll = %g\n", pow(2, p->poll));
256 printf("\tprecision = %g\n", pow(2, p->precision));
257 printf("\trtdelay = %-.16g\n", NTP32asDOUBLE(p->rtdelay));
258 printf("\trtdisp = %-.16g\n", NTP32asDOUBLE(p->rtdisp));
259 printf("\trefid = %x\n", p->refid);
260 printf("\trefts = %-.16g\n", NTP64asDOUBLE(p->refts));
261 printf("\torigts = %-.16g\n", NTP64asDOUBLE(p->origts));
262 printf("\trxts = %-.16g\n", NTP64asDOUBLE(p->rxts));
263 printf("\ttxts = %-.16g\n", NTP64asDOUBLE(p->txts));
264}
265
266void print_ntp_control_message(const ntp_control_message *p) {
267 int i = 0, numpeers = 0;
268 const ntp_assoc_status_pair *peer = NULL;
269
270 printf("control packet contents:\n");
271 printf("\tflags: 0x%.2x , 0x%.2x\n", p->flags, p->op);
272 printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags & LI_MASK);
273 printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags & VN_MASK);
274 printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags & MODE_MASK);
275 printf("\t response=%d (0x%.2x)\n", (p->op & REM_RESP) > 0, p->op & REM_RESP);
276 printf("\t more=%d (0x%.2x)\n", (p->op & REM_MORE) > 0, p->op & REM_MORE);
277 printf("\t error=%d (0x%.2x)\n", (p->op & REM_ERROR) > 0, p->op & REM_ERROR);
278 printf("\t op=%d (0x%.2x)\n", p->op & OP_MASK, p->op & OP_MASK);
279 printf("\tsequence: %d (0x%.2x)\n", ntohs(p->seq), ntohs(p->seq));
280 printf("\tstatus: %d (0x%.2x)\n", ntohs(p->status), ntohs(p->status));
281 printf("\tassoc: %d (0x%.2x)\n", ntohs(p->assoc), ntohs(p->assoc));
282 printf("\toffset: %d (0x%.2x)\n", ntohs(p->offset), ntohs(p->offset));
283 printf("\tcount: %d (0x%.2x)\n", ntohs(p->count), ntohs(p->count));
284 numpeers = ntohs(p->count) / (sizeof(ntp_assoc_status_pair));
285 if (p->op & REM_RESP && p->op & OP_READSTAT) {
286 peer = (ntp_assoc_status_pair *)p->data;
287 for (i = 0; i < numpeers; i++) {
288 printf("\tpeer id %.2x status %.2x", ntohs(peer[i].assoc), ntohs(peer[i].status));
289 if (PEER_SEL(peer[i].status) >= PEER_INCLUDED) {
290 if (PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE) {
291 printf(" <-- current sync source");
292 } else {
293 printf(" <-- current sync candidate");
294 }
295 }
296 printf("\n");
297 }
298 }
299}
300
301void setup_request(ntp_message *p) {
302 struct timeval t;
303
304 memset(p, 0, sizeof(ntp_message));
305 LI_SET(p->flags, LI_ALARM);
306 VN_SET(p->flags, 4);
307 MODE_SET(p->flags, MODE_CLIENT);
308 p->poll = 4;
309 p->precision = (int8_t)0xfa;
310 L16(p->rtdelay) = htons(1);
311 L16(p->rtdisp) = htons(1);
312
313 gettimeofday(&t, NULL);
314 TVtoNTP64(t, p->txts);
315}
316
317/* select the "best" server from a list of servers, and return its index.
318 * this is done by filtering servers based on stratum, dispersion, and
319 * finally round-trip delay. */
320int best_offset_server(const ntp_server_results *slist, int nservers) {
321 int cserver = 0, best_server = -1;
322
323 /* for each server */
324 for (cserver = 0; cserver < nservers; cserver++) {
325 /* We don't want any servers that fails these tests */
326 /* Sort out servers that didn't respond or responede with a 0 stratum;
327 * stratum 0 is for reference clocks so no NTP server should ever report
328 * a stratum 0 */
329 if (slist[cserver].stratum == 0) {
330 if (verbose) {
331 printf("discarding peer %d: stratum=%d\n", cserver, slist[cserver].stratum);
332 }
333 continue;
334 }
335 /* Sort out servers with error flags */
336 if (LI(slist[cserver].flags) == LI_ALARM) {
337 if (verbose) {
338 printf("discarding peer %d: flags=%d\n", cserver, LI(slist[cserver].flags));
339 }
340 continue;
341 }
342
343 /* If we don't have a server yet, use the first one */
344 if (best_server == -1) {
345 best_server = cserver;
346 DBG(printf("using peer %d as our first candidate\n", best_server));
347 continue;
348 }
349
350 /* compare the server to the best one we've seen so far */
351 /* does it have an equal or better stratum? */
352 DBG(printf("comparing peer %d with peer %d\n", cserver, best_server));
353 if (slist[cserver].stratum <= slist[best_server].stratum) {
354 DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server));
355 /* does it have an equal or better dispersion? */
356 if (slist[cserver].rtdisp <= slist[best_server].rtdisp) {
357 DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server));
358 /* does it have a better rtdelay? */
359 if (slist[cserver].rtdelay < slist[best_server].rtdelay) {
360 DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server));
361 best_server = cserver;
362 DBG(printf("peer %d is now our best candidate\n", best_server));
363 }
364 }
365 }
366 }
367
368 if (best_server >= 0) {
369 DBG(printf("best server selected: peer %d\n", best_server));
370 return best_server;
371 } else {
372 DBG(printf("no peers meeting synchronization criteria :(\n"));
373 return -1;
374 }
375}
376
377/* do everything we need to get the total average offset
378 * - we use a certain amount of parallelization with poll() to ensure
379 * we don't waste time sitting around waiting for single packets.
380 * - we also "manually" handle resolving host names and connecting, because
381 * we have to do it in a way that our lazy macros don't handle currently :( */
382double offset_request(const char *host, int *status) {
383 int i = 0, ga_result = 0, num_hosts = 0, *socklist = NULL, respnum = 0;
384 int servers_completed = 0, one_read = 0, servers_readable = 0, best_index = -1;
385 time_t now_time = 0, start_ts = 0;
386 ntp_message *req = NULL;
387 double avg_offset = 0.;
388 struct timeval recv_time;
389 struct addrinfo *ai = NULL, *ai_tmp = NULL, hints;
390 struct pollfd *ufds = NULL;
391 ntp_server_results *servers = NULL;
392
393 /* setup hints to only return results from getaddrinfo that we'd like */
394 memset(&hints, 0, sizeof(struct addrinfo));
395 hints.ai_family = address_family;
396 hints.ai_protocol = IPPROTO_UDP;
397 hints.ai_socktype = SOCK_DGRAM;
398
399 /* fill in ai with the list of hosts resolved by the host name */
400 ga_result = getaddrinfo(host, "123", &hints, &ai);
401 if (ga_result != 0) {
402 die(STATE_UNKNOWN, "error getting address for %s: %s\n", host, gai_strerror(ga_result));
403 }
404
405 /* count the number of returned hosts, and allocate stuff accordingly */
406 for (ai_tmp = ai; ai_tmp != NULL; ai_tmp = ai_tmp->ai_next) {
407 num_hosts++;
408 }
409 req = (ntp_message *)malloc(sizeof(ntp_message) * num_hosts);
410 if (req == NULL) {
411 die(STATE_UNKNOWN, "can not allocate ntp message array");
412 }
413 socklist = (int *)malloc(sizeof(int) * num_hosts);
414 if (socklist == NULL) {
415 die(STATE_UNKNOWN, "can not allocate socket array");
416 }
417 ufds = (struct pollfd *)malloc(sizeof(struct pollfd) * num_hosts);
418 if (ufds == NULL) {
419 die(STATE_UNKNOWN, "can not allocate socket array");
420 }
421 servers = (ntp_server_results *)malloc(sizeof(ntp_server_results) * num_hosts);
422 if (servers == NULL) {
423 die(STATE_UNKNOWN, "can not allocate server array");
424 }
425 memset(servers, 0, sizeof(ntp_server_results) * num_hosts);
426 DBG(printf("Found %d peers to check\n", num_hosts));
427
428 /* setup each socket for writing, and the corresponding struct pollfd */
429 ai_tmp = ai;
430 for (i = 0; ai_tmp; i++) {
431 socklist[i] = socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP);
432 if (socklist[i] == -1) {
433 perror(NULL);
434 die(STATE_UNKNOWN, "can not create new socket");
435 }
436 if (connect(socklist[i], ai_tmp->ai_addr, ai_tmp->ai_addrlen)) {
437 /* don't die here, because it is enough if there is one server
438 answering in time. This also would break for dual ipv4/6 stacked
439 ntp servers when the client only supports on of them.
440 */
441 DBG(printf("can't create socket connection on peer %i: %s\n", i, strerror(errno)));
442 } else {
443 ufds[i].fd = socklist[i];
444 ufds[i].events = POLLIN;
445 ufds[i].revents = 0;
446 }
447 ai_tmp = ai_tmp->ai_next;
448 }
449
450 /* now do AVG_NUM checks to each host. we stop before timeout/2 seconds
451 * have passed in order to ensure post-processing and jitter time. */
452 now_time = start_ts = time(NULL);
453 while (servers_completed < num_hosts && now_time - start_ts <= socket_timeout / 2) {
454 /* loop through each server and find each one which hasn't
455 * been touched in the past second or so and is still lacking
456 * some responses. for each of these servers, send a new request,
457 * and update the "waiting" timestamp with the current time. */
458 now_time = time(NULL);
459
460 for (i = 0; i < num_hosts; i++) {
461 if (servers[i].waiting < now_time && servers[i].num_responses < AVG_NUM) {
462 if (verbose && servers[i].waiting != 0) {
463 printf("re-");
464 }
465 if (verbose) {
466 printf("sending request to peer %d\n", i);
467 }
468 setup_request(&req[i]);
469 write(socklist[i], &req[i], sizeof(ntp_message));
470 servers[i].waiting = now_time;
471 break;
472 }
473 }
474
475 /* quickly poll for any sockets with pending data */
476 servers_readable = poll(ufds, num_hosts, 100);
477 if (servers_readable == -1) {
478 perror("polling ntp sockets");
479 die(STATE_UNKNOWN, "communication errors");
480 }
481
482 /* read from any sockets with pending data */
483 for (i = 0; servers_readable && i < num_hosts; i++) {
484 if (ufds[i].revents & POLLIN && servers[i].num_responses < AVG_NUM) {
485 if (verbose) {
486 printf("response from peer %d: ", i);
487 }
488
489 read(ufds[i].fd, &req[i], sizeof(ntp_message));
490 gettimeofday(&recv_time, NULL);
491 DBG(print_ntp_message(&req[i]));
492 respnum = servers[i].num_responses++;
493 servers[i].offset[respnum] = calc_offset(&req[i], &recv_time);
494 if (verbose) {
495 printf("offset %.10g\n", servers[i].offset[respnum]);
496 }
497 servers[i].stratum = req[i].stratum;
498 servers[i].rtdisp = NTP32asDOUBLE(req[i].rtdisp);
499 servers[i].rtdelay = NTP32asDOUBLE(req[i].rtdelay);
500 servers[i].waiting = 0;
501 servers[i].flags = req[i].flags;
502 servers_readable--;
503 one_read = 1;
504 if (servers[i].num_responses == AVG_NUM) {
505 servers_completed++;
506 }
507 }
508 }
509 /* lather, rinse, repeat. */
510 }
511
512 if (one_read == 0) {
513 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n");
514 }
515
516 /* now, pick the best server from the list */
517 best_index = best_offset_server(servers, num_hosts);
518 if (best_index < 0) {
519 *status = STATE_UNKNOWN;
520 } else {
521 /* finally, calculate the average offset */
522 for (i = 0; i < servers[best_index].num_responses; i++) {
523 avg_offset += servers[best_index].offset[i];
524 }
525 avg_offset /= servers[best_index].num_responses;
526 }
527
528 /* cleanup */
529 /* FIXME: Not closing the socket to avoid reuse of the local port
530 * which can cause old NTP packets to be read instead of NTP control
531 * packets in jitter_request(). THERE MUST BE ANOTHER WAY...
532 * for(j=0; j<num_hosts; j++){ close(socklist[j]); } */
533 free(socklist);
534 free(ufds);
535 free(servers);
536 free(req);
537 freeaddrinfo(ai);
538
539 if (verbose) {
540 printf("overall average offset: %.10g\n", avg_offset);
541 }
542 return avg_offset;
543}
544
545void setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq) {
546 memset(p, 0, sizeof(ntp_control_message));
547 LI_SET(p->flags, LI_NOWARNING);
548 VN_SET(p->flags, VN_RESERVED);
549 MODE_SET(p->flags, MODE_CONTROLMSG);
550 OP_SET(p->op, opcode);
551 p->seq = htons(seq);
552 /* Remaining fields are zero for requests */
553}
554
555/* XXX handle responses with the error bit set */
556double jitter_request(int *status) {
557 int conn = -1, i, npeers = 0, num_candidates = 0;
558 bool syncsource_found = false;
559 int run = 0, min_peer_sel = PEER_INCLUDED, num_selected = 0, num_valid = 0;
560 int peers_size = 0, peer_offset = 0;
561 ntp_assoc_status_pair *peers = NULL;
562 ntp_control_message req;
563 const char *getvar = "jitter";
564 double rval = 0.0, jitter = -1.0;
565 char *startofvalue = NULL, *nptr = NULL;
566 void *tmp;
567
568 /* Long-winded explanation:
569 * Getting the jitter requires a number of steps:
570 * 1) Send a READSTAT request.
571 * 2) Interpret the READSTAT reply
572 * a) The data section contains a list of peer identifiers (16 bits)
573 * and associated status words (16 bits)
574 * b) We want the value of 0x06 in the SEL (peer selection) value,
575 * which means "current synchronizatin source". If that's missing,
576 * we take anything better than 0x04 (see the rfc for details) but
577 * set a minimum of warning.
578 * 3) Send a READVAR request for information on each peer identified
579 * in 2b greater than the minimum selection value.
580 * 4) Extract the jitter value from the data[] (it's ASCII)
581 */
582 my_udp_connect(server_address, 123, &conn);
583
584 /* keep sending requests until the server stops setting the
585 * REM_MORE bit, though usually this is only 1 packet. */
586 do {
587 setup_control_request(&req, OP_READSTAT, 1);
588 DBG(printf("sending READSTAT request"));
589 write(conn, &req, SIZEOF_NTPCM(req));
590 DBG(print_ntp_control_message(&req));
591 /* Attempt to read the largest size packet possible */
592 req.count = htons(MAX_CM_SIZE);
593 DBG(printf("receiving READSTAT response"))
594 read(conn, &req, SIZEOF_NTPCM(req));
595 DBG(print_ntp_control_message(&req));
596 /* Each peer identifier is 4 bytes in the data section, which
597 * we represent as a ntp_assoc_status_pair datatype.
598 */
599 peers_size += ntohs(req.count);
600 if ((tmp = realloc(peers, peers_size)) == NULL) {
601 free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n");
602 }
603 peers = tmp;
604 memcpy((void *)((ptrdiff_t)peers + peer_offset), (void *)req.data, ntohs(req.count));
605 npeers = peers_size / sizeof(ntp_assoc_status_pair);
606 peer_offset += ntohs(req.count);
607 } while (req.op & REM_MORE);
608
609 /* first, let's find out if we have a sync source, or if there are
610 * at least some candidates. in the case of the latter we'll issue
611 * a warning but go ahead with the check on them. */
612 for (i = 0; i < npeers; i++) {
613 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED) {
614 num_candidates++;
615 if (PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE) {
616 syncsource_found = true;
617 min_peer_sel = PEER_SYNCSOURCE;
618 }
619 }
620 }
621 if (verbose) {
622 printf("%d candidate peers available\n", num_candidates);
623 }
624 if (verbose && syncsource_found) {
625 printf("synchronization source found\n");
626 }
627 if (!syncsource_found) {
628 *status = STATE_UNKNOWN;
629 if (verbose) {
630 printf("warning: no synchronization source found\n");
631 }
632 }
633
634 for (run = 0; run < AVG_NUM; run++) {
635 if (verbose) {
636 printf("jitter run %d of %d\n", run + 1, AVG_NUM);
637 }
638 for (i = 0; i < npeers; i++) {
639 /* Only query this server if it is the current sync source */
640 if (PEER_SEL(peers[i].status) >= min_peer_sel) {
641 char jitter_data[MAX_CM_SIZE + 1];
642 size_t jitter_data_count;
643
644 num_selected++;
645 setup_control_request(&req, OP_READVAR, 2);
646 req.assoc = peers[i].assoc;
647 /* By spec, putting the variable name "jitter" in the request
648 * should cause the server to provide _only_ the jitter value.
649 * thus reducing net traffic, guaranteeing us only a single
650 * datagram in reply, and making interpretation much simpler
651 */
652 /* Older servers doesn't know what jitter is, so if we get an
653 * error on the first pass we redo it with "dispersion" */
654 strncpy(req.data, getvar, MAX_CM_SIZE - 1);
655 req.count = htons(strlen(getvar));
656 DBG(printf("sending READVAR request...\n"));
657 write(conn, &req, SIZEOF_NTPCM(req));
658 DBG(print_ntp_control_message(&req));
659
660 req.count = htons(MAX_CM_SIZE);
661 DBG(printf("receiving READVAR response...\n"));
662 read(conn, &req, SIZEOF_NTPCM(req));
663 DBG(print_ntp_control_message(&req));
664
665 if (req.op & REM_ERROR && strstr(getvar, "jitter")) {
666 if (verbose) {
667 printf("The 'jitter' command failed (old ntp server?)\nRestarting with "
668 "'dispersion'...\n");
669 }
670 getvar = "dispersion";
671 num_selected--;
672 i--;
673 continue;
674 }
675
676 /* get to the float value */
677 if (verbose) {
678 printf("parsing jitter from peer %.2x: ", ntohs(peers[i].assoc));
679 }
680 if ((jitter_data_count = ntohs(req.count)) >= sizeof(jitter_data)) {
681 die(STATE_UNKNOWN, _("jitter response too large (%lu bytes)\n"),
682 (unsigned long)jitter_data_count);
683 }
684 memcpy(jitter_data, req.data, jitter_data_count);
685 jitter_data[jitter_data_count] = '\0';
686 startofvalue = strchr(jitter_data, '=');
687 if (startofvalue != NULL) {
688 startofvalue++;
689 jitter = strtod(startofvalue, &nptr);
690 }
691 if (startofvalue == NULL || startofvalue == nptr) {
692 printf("warning: unable to read server jitter response.\n");
693 *status = STATE_UNKNOWN;
694 } else {
695 if (verbose) {
696 printf("%g\n", jitter);
697 }
698 num_valid++;
699 rval += jitter;
700 }
701 }
702 }
703 if (verbose) {
704 printf("jitter parsed from %d/%d peers\n", num_valid, num_selected);
705 }
706 }
707
708 rval = num_valid ? rval / num_valid : -1.0;
709
710 close(conn);
711 if (peers != NULL) {
712 free(peers);
713 }
714 /* If we return -1.0, it means no synchronization source was found */
715 return rval;
716}
717
718int process_arguments(int argc, char **argv) {
719 int c;
720 int option = 0;
721 static struct option longopts[] = {
722 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'},
723 {"verbose", no_argument, 0, 'v'}, {"use-ipv4", no_argument, 0, '4'},
724 {"use-ipv6", no_argument, 0, '6'}, {"warning", required_argument, 0, 'w'},
725 {"critical", required_argument, 0, 'c'}, {"jwarn", required_argument, 0, 'j'},
726 {"jcrit", required_argument, 0, 'k'}, {"timeout", required_argument, 0, 't'},
727 {"hostname", required_argument, 0, 'H'}, {0, 0, 0, 0}};
728
729 if (argc < 2) {
730 usage("\n");
731 }
732
733 while (1) {
734 c = getopt_long(argc, argv, "Vhv46w:c:j:k:t:H:", longopts, &option);
735 if (c == -1 || c == EOF || c == 1) {
736 break;
737 }
738
739 switch (c) {
740 case 'h':
741 print_help();
742 exit(STATE_UNKNOWN);
743 break;
744 case 'V':
745 print_revision(progname, NP_VERSION);
746 exit(STATE_UNKNOWN);
747 break;
748 case 'v':
749 verbose++;
750 break;
751 case 'w':
752 do_offset = true;
753 owarn = optarg;
754 break;
755 case 'c':
756 do_offset = true;
757 ocrit = optarg;
758 break;
759 case 'j':
760 do_jitter = true;
761 jwarn = optarg;
762 break;
763 case 'k':
764 do_jitter = true;
765 jcrit = optarg;
766 break;
767 case 'H':
768 if (!is_host(optarg)) {
769 usage2(_("Invalid hostname/address"), optarg);
770 }
771 server_address = strdup(optarg);
772 break;
773 case 't':
774 socket_timeout = atoi(optarg);
775 break;
776 case '4':
777 address_family = AF_INET;
778 break;
779 case '6':
780#ifdef USE_IPV6
781 address_family = AF_INET6;
782#else
783 usage4(_("IPv6 support not available"));
784#endif
785 break;
786 case '?':
787 /* print short usage statement if args not parsable */
788 usage5();
789 break;
790 }
791 }
792
793 if (server_address == NULL) {
794 usage4(_("Hostname was not supplied"));
795 }
796
797 return 0;
798}
799
800char *perfd_offset(double offset) {
801 return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true,
802 offset_thresholds->critical->end, false, 0, false, 0);
803}
804
805char *perfd_jitter(double jitter) {
806 return fperfdata("jitter", jitter, "s", do_jitter, jitter_thresholds->warning->end, do_jitter,
807 jitter_thresholds->critical->end, true, 0, false, 0);
808}
809
810int main(int argc, char *argv[]) {
811 int result, offset_result, jitter_result;
812 double offset = 0, jitter = 0;
813 char *result_line, *perfdata_line;
814
815 setlocale(LC_ALL, "");
816 bindtextdomain(PACKAGE, LOCALEDIR);
817 textdomain(PACKAGE);
818
819 result = offset_result = jitter_result = STATE_OK;
820
821 /* Parse extra opts if any */
822 argv = np_extra_opts(&argc, argv, progname);
823
824 if (process_arguments(argc, argv) == ERROR) {
825 usage4(_("Could not parse arguments"));
826 }
827
828 set_thresholds(&offset_thresholds, owarn, ocrit);
829 set_thresholds(&jitter_thresholds, jwarn, jcrit);
830
831 /* initialize alarm signal handling */
832 signal(SIGALRM, socket_timeout_alarm_handler);
833
834 /* set socket timeout */
835 alarm(socket_timeout);
836
837 offset = offset_request(server_address, &offset_result);
838 /* check_ntp used to always return CRITICAL if offset_result == STATE_UNKNOWN.
839 * Now we'll only do that is the offset thresholds were set */
840 if (do_offset && offset_result == STATE_UNKNOWN) {
841 result = STATE_CRITICAL;
842 } else {
843 result = get_status(fabs(offset), offset_thresholds);
844 }
845
846 /* If not told to check the jitter, we don't even send packets.
847 * jitter is checked using NTP control packets, which not all
848 * servers recognize. Trying to check the jitter on OpenNTPD
849 * (for example) will result in an error
850 */
851 if (do_jitter) {
852 jitter = jitter_request(&jitter_result);
853 result = max_state_alt(result, get_status(jitter, jitter_thresholds));
854 /* -1 indicates that we couldn't calculate the jitter
855 * Only overrides STATE_OK from the offset */
856 if (jitter == -1.0 && result == STATE_OK) {
857 result = STATE_UNKNOWN;
858 }
859 }
860 result = max_state_alt(result, jitter_result);
861
862 switch (result) {
863 case STATE_CRITICAL:
864 xasprintf(&result_line, _("NTP CRITICAL:"));
865 break;
866 case STATE_WARNING:
867 xasprintf(&result_line, _("NTP WARNING:"));
868 break;
869 case STATE_OK:
870 xasprintf(&result_line, _("NTP OK:"));
871 break;
872 default:
873 xasprintf(&result_line, _("NTP UNKNOWN:"));
874 break;
875 }
876 if (offset_result == STATE_UNKNOWN) {
877 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown"));
878 xasprintf(&perfdata_line, "");
879 } else {
880 xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset);
881 xasprintf(&perfdata_line, "%s", perfd_offset(offset));
882 }
883 if (do_jitter) {
884 xasprintf(&result_line, "%s, jitter=%f", result_line, jitter);
885 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_jitter(jitter));
886 }
887 printf("%s|%s\n", result_line, perfdata_line);
888
889 if (server_address != NULL) {
890 free(server_address);
891 }
892 return result;
893}
894
895void print_help(void) {
896 print_revision(progname, NP_VERSION);
897
898 printf("Copyright (c) 2006 Sean Finney\n");
899 printf(COPYRIGHT, copyright, email);
900
901 printf("%s\n", _("This plugin checks the selected ntp server"));
902
903 printf("\n\n");
904
905 print_usage();
906 printf(UT_HELP_VRSN);
907 printf(UT_EXTRA_OPTS);
908 printf(UT_HOST_PORT, 'p', "123");
909 printf(UT_IPv46);
910 printf(" %s\n", "-w, --warning=THRESHOLD");
911 printf(" %s\n", _("Offset to result in warning status (seconds)"));
912 printf(" %s\n", "-c, --critical=THRESHOLD");
913 printf(" %s\n", _("Offset to result in critical status (seconds)"));
914 printf(" %s\n", "-j, --jwarn=THRESHOLD");
915 printf(" %s\n", _("Warning threshold for jitter"));
916 printf(" %s\n", "-k, --jcrit=THRESHOLD");
917 printf(" %s\n", _("Critical threshold for jitter"));
918 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
919 printf(UT_VERBOSE);
920
921 printf("\n");
922 printf("%s\n", _("Notes:"));
923 printf(UT_THRESHOLDS_NOTES);
924
925 printf("\n");
926 printf("%s\n", _("Examples:"));
927 printf(" %s\n", _("Normal offset check:"));
928 printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1"));
929 printf("\n");
930 printf(" %s\n",
931 _("Check jitter too, avoiding critical notifications if jitter isn't available"));
932 printf(" %s\n", _("(See Notes above for more details on thresholds formats):"));
933 printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200"));
934
935 printf(UT_SUPPORT);
936
937 printf("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or"));
938 printf("%s\n\n", _("check_ntp_time instead."));
939}
940
941void print_usage(void) {
942 printf("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or"));
943 printf("%s\n\n", _("check_ntp_time instead."));
944 printf("%s\n", _("Usage:"));
945 printf(" %s -H <host> [-w <warn>] [-c <crit>] [-j <warn>] [-k <crit>] [-4|-6] [-v verbose]\n",
946 progname);
947}
diff --git a/plugins/check_ntp_peer.c b/plugins/check_ntp_peer.c
index 26f74286..b5cfb460 100644
--- a/plugins/check_ntp_peer.c
+++ b/plugins/check_ntp_peer.c
@@ -548,7 +548,7 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
548 break; 548 break;
549 case 'w': { 549 case 'w': {
550 mp_range_parsed tmp = mp_parse_range_string(optarg); 550 mp_range_parsed tmp = mp_parse_range_string(optarg);
551 if (tmp.error != MP_PARSING_SUCCES) { 551 if (tmp.error != MP_PARSING_SUCCESS) {
552 die(STATE_UNKNOWN, "failed to parse warning offset threshold"); 552 die(STATE_UNKNOWN, "failed to parse warning offset threshold");
553 } 553 }
554 554
@@ -557,7 +557,7 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
557 } break; 557 } break;
558 case 'c': { 558 case 'c': {
559 mp_range_parsed tmp = mp_parse_range_string(optarg); 559 mp_range_parsed tmp = mp_parse_range_string(optarg);
560 if (tmp.error != MP_PARSING_SUCCES) { 560 if (tmp.error != MP_PARSING_SUCCESS) {
561 die(STATE_UNKNOWN, "failed to parse critical offset threshold"); 561 die(STATE_UNKNOWN, "failed to parse critical offset threshold");
562 } 562 }
563 563
@@ -567,7 +567,7 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
567 case 'W': { 567 case 'W': {
568 result.config.do_stratum = true; 568 result.config.do_stratum = true;
569 mp_range_parsed tmp = mp_parse_range_string(optarg); 569 mp_range_parsed tmp = mp_parse_range_string(optarg);
570 if (tmp.error != MP_PARSING_SUCCES) { 570 if (tmp.error != MP_PARSING_SUCCESS) {
571 die(STATE_UNKNOWN, "failed to parse warning stratum threshold"); 571 die(STATE_UNKNOWN, "failed to parse warning stratum threshold");
572 } 572 }
573 573
@@ -577,7 +577,7 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
577 case 'C': { 577 case 'C': {
578 result.config.do_stratum = true; 578 result.config.do_stratum = true;
579 mp_range_parsed tmp = mp_parse_range_string(optarg); 579 mp_range_parsed tmp = mp_parse_range_string(optarg);
580 if (tmp.error != MP_PARSING_SUCCES) { 580 if (tmp.error != MP_PARSING_SUCCESS) {
581 die(STATE_UNKNOWN, "failed to parse critical stratum threshold"); 581 die(STATE_UNKNOWN, "failed to parse critical stratum threshold");
582 } 582 }
583 583
@@ -587,7 +587,7 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
587 case 'j': { 587 case 'j': {
588 result.config.do_jitter = true; 588 result.config.do_jitter = true;
589 mp_range_parsed tmp = mp_parse_range_string(optarg); 589 mp_range_parsed tmp = mp_parse_range_string(optarg);
590 if (tmp.error != MP_PARSING_SUCCES) { 590 if (tmp.error != MP_PARSING_SUCCESS) {
591 die(STATE_UNKNOWN, "failed to parse warning jitter threshold"); 591 die(STATE_UNKNOWN, "failed to parse warning jitter threshold");
592 } 592 }
593 593
@@ -597,7 +597,7 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
597 case 'k': { 597 case 'k': {
598 result.config.do_jitter = true; 598 result.config.do_jitter = true;
599 mp_range_parsed tmp = mp_parse_range_string(optarg); 599 mp_range_parsed tmp = mp_parse_range_string(optarg);
600 if (tmp.error != MP_PARSING_SUCCES) { 600 if (tmp.error != MP_PARSING_SUCCESS) {
601 die(STATE_UNKNOWN, "failed to parse critical jitter threshold"); 601 die(STATE_UNKNOWN, "failed to parse critical jitter threshold");
602 } 602 }
603 603
@@ -607,7 +607,7 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
607 case 'm': { 607 case 'm': {
608 result.config.do_truechimers = true; 608 result.config.do_truechimers = true;
609 mp_range_parsed tmp = mp_parse_range_string(optarg); 609 mp_range_parsed tmp = mp_parse_range_string(optarg);
610 if (tmp.error != MP_PARSING_SUCCES) { 610 if (tmp.error != MP_PARSING_SUCCESS) {
611 die(STATE_UNKNOWN, "failed to parse warning truechimer threshold"); 611 die(STATE_UNKNOWN, "failed to parse warning truechimer threshold");
612 } 612 }
613 613
@@ -617,7 +617,7 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
617 case 'n': { 617 case 'n': {
618 result.config.do_truechimers = true; 618 result.config.do_truechimers = true;
619 mp_range_parsed tmp = mp_parse_range_string(optarg); 619 mp_range_parsed tmp = mp_parse_range_string(optarg);
620 if (tmp.error != MP_PARSING_SUCCES) { 620 if (tmp.error != MP_PARSING_SUCCESS) {
621 die(STATE_UNKNOWN, "failed to parse critical truechimer threshold"); 621 die(STATE_UNKNOWN, "failed to parse critical truechimer threshold");
622 } 622 }
623 623
@@ -640,11 +640,7 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
640 address_family = AF_INET; 640 address_family = AF_INET;
641 break; 641 break;
642 case '6': 642 case '6':
643#ifdef USE_IPV6
644 address_family = AF_INET6; 643 address_family = AF_INET6;
645#else
646 usage4(_("IPv6 support not available"));
647#endif
648 break; 644 break;
649 case '?': 645 case '?':
650 /* print short usage statement if args not parsable */ 646 /* print short usage statement if args not parsable */
diff --git a/plugins/check_ntp_time.c b/plugins/check_ntp_time.c
index 1300faea..4e3a55db 100644
--- a/plugins/check_ntp_time.c
+++ b/plugins/check_ntp_time.c
@@ -348,7 +348,6 @@ static offset_request_wrapper offset_request(const char *host, const char *port,
348 is_socket = false; 348 is_socket = false;
349 349
350 /* fill in ai with the list of hosts resolved by the host name */ 350 /* fill in ai with the list of hosts resolved by the host name */
351 struct addrinfo *addresses = NULL;
352 int ga_result = getaddrinfo(host, port, &hints, &addresses); 351 int ga_result = getaddrinfo(host, port, &hints, &addresses);
353 if (ga_result != 0) { 352 if (ga_result != 0) {
354 die(STATE_UNKNOWN, "error getting address for %s: %s\n", host, gai_strerror(ga_result)); 353 die(STATE_UNKNOWN, "error getting address for %s: %s\n", host, gai_strerror(ga_result));
@@ -606,7 +605,7 @@ static check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
606 break; 605 break;
607 case 'w': { 606 case 'w': {
608 mp_range_parsed tmp = mp_parse_range_string(optarg); 607 mp_range_parsed tmp = mp_parse_range_string(optarg);
609 if (tmp.error != MP_PARSING_SUCCES) { 608 if (tmp.error != MP_PARSING_SUCCESS) {
610 die(STATE_UNKNOWN, "failed to parse warning threshold"); 609 die(STATE_UNKNOWN, "failed to parse warning threshold");
611 } 610 }
612 611
@@ -615,7 +614,7 @@ static check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
615 } break; 614 } break;
616 case 'c': { 615 case 'c': {
617 mp_range_parsed tmp = mp_parse_range_string(optarg); 616 mp_range_parsed tmp = mp_parse_range_string(optarg);
618 if (tmp.error != MP_PARSING_SUCCES) { 617 if (tmp.error != MP_PARSING_SUCCESS) {
619 die(STATE_UNKNOWN, "failed to parse crit threshold"); 618 die(STATE_UNKNOWN, "failed to parse crit threshold");
620 } 619 }
621 620
@@ -641,11 +640,7 @@ static check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
641 address_family = AF_INET; 640 address_family = AF_INET;
642 break; 641 break;
643 case '6': 642 case '6':
644#ifdef USE_IPV6
645 address_family = AF_INET6; 643 address_family = AF_INET6;
646#else
647 usage4(_("IPv6 support not available"));
648#endif
649 break; 644 break;
650 case '?': 645 case '?':
651 /* print short usage statement if args not parsable */ 646 /* print short usage statement if args not parsable */
@@ -662,6 +657,14 @@ static check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
662} 657}
663 658
664int main(int argc, char *argv[]) { 659int main(int argc, char *argv[]) {
660#ifdef __OpenBSD__
661 /* - rpath is required to read --extra-opts (given up later)
662 * - inet is required for sockets
663 * - unix is required for Unix domain sockets
664 * - dns is required for name lookups */
665 pledge("stdio rpath inet unix dns", NULL);
666#endif // __OpenBSD__
667
665 setlocale(LC_ALL, ""); 668 setlocale(LC_ALL, "");
666 bindtextdomain(PACKAGE, LOCALEDIR); 669 bindtextdomain(PACKAGE, LOCALEDIR);
667 textdomain(PACKAGE); 670 textdomain(PACKAGE);
@@ -675,6 +678,10 @@ int main(int argc, char *argv[]) {
675 usage4(_("Could not parse arguments")); 678 usage4(_("Could not parse arguments"));
676 } 679 }
677 680
681#ifdef __OpenBSD__
682 pledge("stdio inet unix dns", NULL);
683#endif // __OpenBSD__
684
678 const check_ntp_time_config config = tmp_config.config; 685 const check_ntp_time_config config = tmp_config.config;
679 686
680 if (config.output_format_is_set) { 687 if (config.output_format_is_set) {
diff --git a/plugins/check_pgsql.c b/plugins/check_pgsql.c
index 0ce75e0a..8cbaaeeb 100644
--- a/plugins/check_pgsql.c
+++ b/plugins/check_pgsql.c
@@ -401,7 +401,7 @@ static check_pgsql_config_wrapper process_arguments(int argc, char **argv) {
401 break; 401 break;
402 case 'c': /* critical time threshold */ { 402 case 'c': /* critical time threshold */ {
403 mp_range_parsed tmp = mp_parse_range_string(optarg); 403 mp_range_parsed tmp = mp_parse_range_string(optarg);
404 if (tmp.error != MP_PARSING_SUCCES) { 404 if (tmp.error != MP_PARSING_SUCCESS) {
405 die(STATE_UNKNOWN, "failed to parse critical time threshold"); 405 die(STATE_UNKNOWN, "failed to parse critical time threshold");
406 } 406 }
407 result.config.time_thresholds = 407 result.config.time_thresholds =
@@ -409,7 +409,7 @@ static check_pgsql_config_wrapper process_arguments(int argc, char **argv) {
409 } break; 409 } break;
410 case 'w': /* warning time threshold */ { 410 case 'w': /* warning time threshold */ {
411 mp_range_parsed tmp = mp_parse_range_string(optarg); 411 mp_range_parsed tmp = mp_parse_range_string(optarg);
412 if (tmp.error != MP_PARSING_SUCCES) { 412 if (tmp.error != MP_PARSING_SUCCESS) {
413 die(STATE_UNKNOWN, "failed to parse warning time threshold"); 413 die(STATE_UNKNOWN, "failed to parse warning time threshold");
414 } 414 }
415 result.config.time_thresholds = 415 result.config.time_thresholds =
@@ -417,7 +417,7 @@ static check_pgsql_config_wrapper process_arguments(int argc, char **argv) {
417 } break; 417 } break;
418 case 'C': /* critical query threshold */ { 418 case 'C': /* critical query threshold */ {
419 mp_range_parsed tmp = mp_parse_range_string(optarg); 419 mp_range_parsed tmp = mp_parse_range_string(optarg);
420 if (tmp.error != MP_PARSING_SUCCES) { 420 if (tmp.error != MP_PARSING_SUCCESS) {
421 die(STATE_UNKNOWN, "failed to parse critical query threshold"); 421 die(STATE_UNKNOWN, "failed to parse critical query threshold");
422 } 422 }
423 423
@@ -427,7 +427,7 @@ static check_pgsql_config_wrapper process_arguments(int argc, char **argv) {
427 } break; 427 } break;
428 case 'W': /* warning query threshold */ { 428 case 'W': /* warning query threshold */ {
429 mp_range_parsed tmp = mp_parse_range_string(optarg); 429 mp_range_parsed tmp = mp_parse_range_string(optarg);
430 if (tmp.error != MP_PARSING_SUCCES) { 430 if (tmp.error != MP_PARSING_SUCCESS) {
431 die(STATE_UNKNOWN, "failed to parse warning query threshold"); 431 die(STATE_UNKNOWN, "failed to parse warning query threshold");
432 } 432 }
433 result.config.qthresholds = 433 result.config.qthresholds =
diff --git a/plugins/check_ping.c b/plugins/check_ping.c
index 61feb958..e1ee0f5c 100644
--- a/plugins/check_ping.c
+++ b/plugins/check_ping.c
@@ -246,11 +246,7 @@ check_ping_config_wrapper process_arguments(int argc, char **argv) {
246 address_family = AF_INET; 246 address_family = AF_INET;
247 break; 247 break;
248 case '6': /* IPv6 only */ 248 case '6': /* IPv6 only */
249#ifdef USE_IPV6
250 address_family = AF_INET6; 249 address_family = AF_INET6;
251#else
252 usage(_("IPv6 support not available\n"));
253#endif
254 break; 250 break;
255 case 'H': /* hostname */ { 251 case 'H': /* hostname */ {
256 char *ptr = optarg; 252 char *ptr = optarg;
diff --git a/plugins/check_procs.c b/plugins/check_procs.c
index ae6e9c23..50837cb4 100644
--- a/plugins/check_procs.c
+++ b/plugins/check_procs.c
@@ -430,7 +430,7 @@ check_procs_config_wrapper process_arguments(int argc, char **argv) {
430 while (true) { 430 while (true) {
431 int option = 0; 431 int option = 0;
432 int option_index = 432 int option_index =
433 getopt_long(argc, argv, "Vvhkt:c:w:p:s:u:C:a:z:r:m:P:T:X:", longopts, &option); 433 getopt_long(argc, argv, "Vvhkt:c:w:p:s:u:C:a:z:r:m:P:TX:", longopts, &option);
434 434
435 if (option_index == -1 || option_index == EOF) { 435 if (option_index == -1 || option_index == EOF) {
436 break; 436 break;
diff --git a/plugins/check_radius.c b/plugins/check_radius.c
index d26f7cf3..f20af660 100644
--- a/plugins/check_radius.c
+++ b/plugins/check_radius.c
@@ -28,6 +28,7 @@
28 * 28 *
29 *****************************************************************************/ 29 *****************************************************************************/
30 30
31#include "output.h"
31const char *progname = "check_radius"; 32const char *progname = "check_radius";
32const char *copyright = "2000-2024"; 33const char *copyright = "2000-2024";
33const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
@@ -61,6 +62,8 @@ void print_usage(void);
61# define my_rc_conf_str(a) rc_conf_str(rch, a) 62# define my_rc_conf_str(a) rc_conf_str(rch, a)
62# if defined(HAVE_LIBRADCLI) 63# if defined(HAVE_LIBRADCLI)
63# define my_rc_send_server(a, b) rc_send_server(rch, a, b, AUTH) 64# define my_rc_send_server(a, b) rc_send_server(rch, a, b, AUTH)
65# elif defined(HAVE_LIBFREERADIUS_CLIENT)
66# define my_rc_send_server(a, b) rc_send_server(rch, a, b, 0)
64# else 67# else
65# define my_rc_send_server(a, b) rc_send_server(rch, a, b) 68# define my_rc_send_server(a, b) rc_send_server(rch, a, b)
66# endif 69# endif
@@ -158,49 +161,80 @@ int main(int argc, char **argv) {
158 161
159 check_radius_config config = tmp_config.config; 162 check_radius_config config = tmp_config.config;
160 163
164 if (config.output_format_is_set) {
165 mp_set_format(config.output_format);
166 }
167
161#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || \ 168#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || \
162 defined(HAVE_LIBRADCLI) 169 defined(HAVE_LIBRADCLI)
163 rc_handle *rch = NULL; 170 rc_handle *rch = NULL;
164#endif 171#endif
165 172
173 mp_check overall = mp_check_init();
174 mp_subcheck sc_read_config = mp_subcheck_init();
175
166 char *str = strdup("dictionary"); 176 char *str = strdup("dictionary");
167 if ((config.config_file && my_rc_read_config(config.config_file, &rch)) || 177 if ((config.config_file && my_rc_read_config(config.config_file, &rch)) ||
168 my_rc_read_dictionary(my_rc_conf_str(str))) { 178 my_rc_read_dictionary(my_rc_conf_str(str))) {
169 die(STATE_UNKNOWN, _("Config file error\n")); 179 sc_read_config = mp_set_subcheck_state(sc_read_config, STATE_UNKNOWN);
180 xasprintf(&sc_read_config.output, "failed to read config file");
181 mp_add_subcheck_to_check(&overall, sc_read_config);
182 mp_exit(overall);
170 } 183 }
171 184
185 sc_read_config = mp_set_subcheck_state(sc_read_config, STATE_OK);
186 xasprintf(&sc_read_config.output, "read config file successfully");
187 mp_add_subcheck_to_check(&overall, sc_read_config);
188
172 uint32_t service = PW_AUTHENTICATE_ONLY; 189 uint32_t service = PW_AUTHENTICATE_ONLY;
173 190
191 mp_subcheck sc_configuring = mp_subcheck_init();
174 SEND_DATA data; 192 SEND_DATA data;
175 memset(&data, 0, sizeof(data)); 193 memset(&data, 0, sizeof(data));
176 if (!(my_rc_avpair_add(&data.send_pairs, PW_SERVICE_TYPE, &service, 0) && 194 if (!(my_rc_avpair_add(&data.send_pairs, PW_SERVICE_TYPE, &service, 0) &&
177 my_rc_avpair_add(&data.send_pairs, PW_USER_NAME, config.username, 0) && 195 my_rc_avpair_add(&data.send_pairs, PW_USER_NAME, config.username, 0) &&
178 my_rc_avpair_add(&data.send_pairs, PW_USER_PASSWORD, config.password, 0))) { 196 my_rc_avpair_add(&data.send_pairs, PW_USER_PASSWORD, config.password, 0))) {
179 die(STATE_UNKNOWN, _("Out of Memory?\n")); 197 xasprintf(&sc_configuring.output, "Failed to the radius options: Out of Memory?");
198 sc_configuring = mp_set_subcheck_state(sc_configuring, STATE_UNKNOWN);
199 mp_add_subcheck_to_check(&overall, sc_configuring);
200 mp_exit(overall);
180 } 201 }
181 202
182 if (config.nas_id != NULL) { 203 if (config.nas_id != NULL) {
183 if (!(my_rc_avpair_add(&data.send_pairs, PW_NAS_IDENTIFIER, config.nas_id, 0))) { 204 if (!(my_rc_avpair_add(&data.send_pairs, PW_NAS_IDENTIFIER, config.nas_id, 0))) {
184 die(STATE_UNKNOWN, _("Invalid NAS-Identifier\n")); 205 xasprintf(&sc_configuring.output,
206 "Failed to the radius options: invalid NAS identifier?");
207 sc_configuring = mp_set_subcheck_state(sc_configuring, STATE_UNKNOWN);
208 mp_add_subcheck_to_check(&overall, sc_configuring);
209 mp_exit(overall);
185 } 210 }
186 } 211 }
187 212
188 char name[HOST_NAME_MAX]; 213 char name[HOST_NAME_MAX];
189 if (config.nas_ip_address == NULL) { 214 if (config.nas_ip_address == NULL) {
190 if (gethostname(name, sizeof(name)) != 0) { 215 if (gethostname(name, sizeof(name)) != 0) {
191 die(STATE_UNKNOWN, _("gethostname() failed!\n")); 216 xasprintf(&sc_configuring.output, "gethostname() failed");
217 sc_configuring = mp_set_subcheck_state(sc_configuring, STATE_UNKNOWN);
218 mp_add_subcheck_to_check(&overall, sc_configuring);
219 mp_exit(overall);
192 } 220 }
193 config.nas_ip_address = name; 221 config.nas_ip_address = name;
194 } 222 }
195 223
196 struct sockaddr_storage radius_server_socket; 224 struct sockaddr_storage radius_server_socket;
197 if (!dns_lookup(config.nas_ip_address, &radius_server_socket, AF_UNSPEC)) { 225 if (!dns_lookup(config.nas_ip_address, &radius_server_socket, AF_UNSPEC)) {
198 die(STATE_UNKNOWN, _("Invalid NAS-IP-Address\n")); 226 xasprintf(&sc_configuring.output, "invalid NAS IP address. Lookup failed");
227 sc_configuring = mp_set_subcheck_state(sc_configuring, STATE_UNKNOWN);
228 mp_add_subcheck_to_check(&overall, sc_configuring);
229 mp_exit(overall);
199 } 230 }
200 231
201 uint32_t client_id = ntohl(((struct sockaddr_in *)&radius_server_socket)->sin_addr.s_addr); 232 uint32_t client_id = ntohl(((struct sockaddr_in *)&radius_server_socket)->sin_addr.s_addr);
202 if (my_rc_avpair_add(&(data.send_pairs), PW_NAS_IP_ADDRESS, &client_id, 0) == NULL) { 233 if (my_rc_avpair_add(&(data.send_pairs), PW_NAS_IP_ADDRESS, &client_id, 0) == NULL) {
203 die(STATE_UNKNOWN, _("Invalid NAS-IP-Address\n")); 234 xasprintf(&sc_configuring.output, "invalid NAS IP address. Setting option failed");
235 sc_configuring = mp_set_subcheck_state(sc_configuring, STATE_UNKNOWN);
236 mp_add_subcheck_to_check(&overall, sc_configuring);
237 mp_exit(overall);
204 } 238 }
205 239
206 my_rc_buildreq(&data, PW_ACCESS_REQUEST, config.server, config.port, (int)timeout_interval, 240 my_rc_buildreq(&data, PW_ACCESS_REQUEST, config.server, config.port, (int)timeout_interval,
@@ -218,51 +252,78 @@ int main(int argc, char **argv) {
218 rc_avpair_free(data.receive_pairs); 252 rc_avpair_free(data.receive_pairs);
219 } 253 }
220 254
255 mp_subcheck sc_eval = mp_subcheck_init();
256
221 if (result == TIMEOUT_RC) { 257 if (result == TIMEOUT_RC) {
222 printf("Timeout\n"); 258 xasprintf(&sc_eval.output, "timeout");
223 exit(STATE_CRITICAL); 259 sc_eval = mp_set_subcheck_state(sc_eval, STATE_CRITICAL);
260 mp_add_subcheck_to_check(&overall, sc_eval);
261 mp_exit(overall);
224 } 262 }
225 263
226 if (result == ERROR_RC) { 264 if (result == ERROR_RC) {
227 printf(_("Auth Error\n")); 265 xasprintf(&sc_eval.output, "auth error");
228 exit(STATE_CRITICAL); 266 sc_eval = mp_set_subcheck_state(sc_eval, STATE_CRITICAL);
267 mp_add_subcheck_to_check(&overall, sc_eval);
268 mp_exit(overall);
229 } 269 }
230 270
231 if (result == REJECT_RC) { 271 if (result == REJECT_RC) {
232 printf(_("Auth Failed\n")); 272 xasprintf(&sc_eval.output, "auth failed");
233 exit(STATE_WARNING); 273 sc_eval = mp_set_subcheck_state(sc_eval, STATE_WARNING);
274 mp_add_subcheck_to_check(&overall, sc_eval);
275 mp_exit(overall);
234 } 276 }
235 277
236 if (result == BADRESP_RC) { 278 if (result == BADRESP_RC) {
237 printf(_("Bad Response\n")); 279 xasprintf(&sc_eval.output, "bad response");
238 exit(STATE_WARNING); 280 sc_eval = mp_set_subcheck_state(sc_eval, STATE_WARNING);
281 mp_add_subcheck_to_check(&overall, sc_eval);
282 mp_exit(overall);
239 } 283 }
240 284
241 if (config.expect && !strstr(msg, config.expect)) { 285 if (config.expect && !strstr(msg, config.expect)) {
242 printf("%s\n", msg); 286 xasprintf(&sc_eval.output, "%s", msg);
243 exit(STATE_WARNING); 287 sc_eval = mp_set_subcheck_state(sc_eval, STATE_WARNING);
288 mp_add_subcheck_to_check(&overall, sc_eval);
289 mp_exit(overall);
244 } 290 }
245 291
246 if (result == OK_RC) { 292 if (result == OK_RC) {
247 printf(_("Auth OK\n")); 293 xasprintf(&sc_eval.output, "auth OK");
248 exit(STATE_OK); 294 sc_eval = mp_set_subcheck_state(sc_eval, STATE_OK);
295 mp_add_subcheck_to_check(&overall, sc_eval);
296 mp_exit(overall);
249 } 297 }
250 298
251 (void)snprintf(msg, sizeof(msg), _("Unexpected result code %d"), result); 299 xasprintf(&sc_eval.output, "unexpected result code: %d", result);
252 printf("%s\n", msg); 300 sc_eval = mp_set_subcheck_state(sc_eval, STATE_UNKNOWN);
253 exit(STATE_UNKNOWN); 301 mp_add_subcheck_to_check(&overall, sc_eval);
302
303 mp_exit(overall);
254} 304}
255 305
256/* process command-line arguments */ 306/* process command-line arguments */
257check_radius_config_wrapper process_arguments(int argc, char **argv) { 307check_radius_config_wrapper process_arguments(int argc, char **argv) {
258 static struct option longopts[] = { 308 enum {
259 {"hostname", required_argument, 0, 'H'}, {"port", required_argument, 0, 'P'}, 309 output_format_index
260 {"username", required_argument, 0, 'u'}, {"password", required_argument, 0, 'p'}, 310 };
261 {"nas-id", required_argument, 0, 'n'}, {"nas-ip-address", required_argument, 0, 'N'}, 311
262 {"filename", required_argument, 0, 'F'}, {"expect", required_argument, 0, 'e'}, 312 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
263 {"retries", required_argument, 0, 'r'}, {"timeout", required_argument, 0, 't'}, 313 {"port", required_argument, 0, 'P'},
264 {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, 314 {"username", required_argument, 0, 'u'},
265 {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; 315 {"password", required_argument, 0, 'p'},
316 {"nas-id", required_argument, 0, 'n'},
317 {"nas-ip-address", required_argument, 0, 'N'},
318 {"filename", required_argument, 0, 'F'},
319 {"expect", required_argument, 0, 'e'},
320 {"retries", required_argument, 0, 'r'},
321 {"timeout", required_argument, 0, 't'},
322 {"verbose", no_argument, 0, 'v'},
323 {"version", no_argument, 0, 'V'},
324 {"help", no_argument, 0, 'h'},
325 {"output-format", required_argument, 0, output_format_index},
326 {0, 0, 0, 0}};
266 327
267 check_radius_config_wrapper result = { 328 check_radius_config_wrapper result = {
268 .errorcode = OK, 329 .errorcode = OK,
@@ -340,6 +401,18 @@ check_radius_config_wrapper process_arguments(int argc, char **argv) {
340 usage2(_("Timeout interval must be a positive integer"), optarg); 401 usage2(_("Timeout interval must be a positive integer"), optarg);
341 } 402 }
342 break; 403 break;
404 case output_format_index: {
405 parsed_output_format parser = mp_parse_output_format(optarg);
406 if (!parser.parsing_success) {
407 // TODO List all available formats here, maybe add anothoer usage function
408 printf("Invalid output format: %s\n", optarg);
409 exit(STATE_UNKNOWN);
410 }
411
412 result.config.output_format_is_set = true;
413 result.config.output_format = parser.output_format;
414 break;
415 }
343 } 416 }
344 } 417 }
345 418
@@ -393,6 +466,7 @@ void print_help(void) {
393 printf(" %s\n", _("Response string to expect from the server")); 466 printf(" %s\n", _("Response string to expect from the server"));
394 printf(" %s\n", "-r, --retries=INTEGER"); 467 printf(" %s\n", "-r, --retries=INTEGER");
395 printf(" %s\n", _("Number of times to retry a failed connection")); 468 printf(" %s\n", _("Number of times to retry a failed connection"));
469 printf(UT_OUTPUT_FORMAT);
396 470
397 printf(UT_CONN_TIMEOUT, timeout_interval); 471 printf(UT_CONN_TIMEOUT, timeout_interval);
398 472
diff --git a/plugins/check_radius.d/config.h b/plugins/check_radius.d/config.h
index b27d31e7..656bf98e 100644
--- a/plugins/check_radius.d/config.h
+++ b/plugins/check_radius.d/config.h
@@ -1,6 +1,7 @@
1#pragma once 1#pragma once
2 2
3#include "../../config.h" 3#include "../../config.h"
4#include "output.h"
4#include <stddef.h> 5#include <stddef.h>
5#if defined(HAVE_LIBRADCLI) 6#if defined(HAVE_LIBRADCLI)
6# include <radcli/radcli.h> 7# include <radcli/radcli.h>
@@ -23,6 +24,9 @@ typedef struct {
23 unsigned short port; 24 unsigned short port;
24 25
25 char *expect; 26 char *expect;
27
28 bool output_format_is_set;
29 mp_output_format output_format;
26} check_radius_config; 30} check_radius_config;
27 31
28check_radius_config check_radius_config_init() { 32check_radius_config check_radius_config_init() {
@@ -37,6 +41,8 @@ check_radius_config check_radius_config_init() {
37 .port = PW_AUTH_UDP_PORT, 41 .port = PW_AUTH_UDP_PORT,
38 42
39 .expect = NULL, 43 .expect = NULL,
44
45 .output_format_is_set = false,
40 }; 46 };
41 return tmp; 47 return tmp;
42} 48}
diff --git a/plugins/check_real.c b/plugins/check_real.c
index 66d07f8f..b415578f 100644
--- a/plugins/check_real.c
+++ b/plugins/check_real.c
@@ -28,19 +28,21 @@
28 * 28 *
29 *****************************************************************************/ 29 *****************************************************************************/
30 30
31#include "output.h"
32#include "perfdata.h"
31#include "states.h" 33#include "states.h"
32#include <stdio.h> 34#include <stdio.h>
33const char *progname = "check_real";
34const char *copyright = "2000-2024";
35const char *email = "devel@monitoring-plugins.org";
36
37#include "common.h" 35#include "common.h"
38#include "netutils.h" 36#include "netutils.h"
37#include "thresholds.h"
39#include "utils.h" 38#include "utils.h"
40#include "check_real.d/config.h" 39#include "check_real.d/config.h"
41 40
42#define EXPECT "RTSP/1." 41const char *progname = "check_real";
43#define URL "" 42const char *copyright = "2000-2024";
43const char *email = "devel@monitoring-plugins.org";
44
45#define URL ""
44 46
45typedef struct { 47typedef struct {
46 int errorcode; 48 int errorcode;
@@ -68,42 +70,68 @@ int main(int argc, char **argv) {
68 70
69 const check_real_config config = tmp_config.config; 71 const check_real_config config = tmp_config.config;
70 72
73 if (config.output_format_is_set) {
74 mp_set_format(config.output_format);
75 }
76
71 /* initialize alarm signal handling */ 77 /* initialize alarm signal handling */
72 signal(SIGALRM, socket_timeout_alarm_handler); 78 signal(SIGALRM, socket_timeout_alarm_handler);
73 79
74 /* set socket timeout */ 80 /* set socket timeout */
75 alarm(socket_timeout); 81 alarm(socket_timeout);
82 time_t start_time;
76 time(&start_time); 83 time(&start_time);
77 84
85 mp_check overall = mp_check_init();
86 mp_subcheck sc_connect = mp_subcheck_init();
87
78 /* try to connect to the host at the given port number */ 88 /* try to connect to the host at the given port number */
79 int socket; 89 int socket;
80 if (my_tcp_connect(config.server_address, config.server_port, &socket) != STATE_OK) { 90 if (my_tcp_connect(config.server_address, config.server_port, &socket) != STATE_OK) {
81 die(STATE_CRITICAL, _("Unable to connect to %s on port %d\n"), config.server_address, 91 xasprintf(&sc_connect.output, _("unable to connect to %s on port %d"),
82 config.server_port); 92 config.server_address, config.server_port);
93 sc_connect = mp_set_subcheck_state(sc_connect, STATE_CRITICAL);
94 mp_add_subcheck_to_check(&overall, sc_connect);
95 mp_exit(overall);
83 } 96 }
84 97
98 xasprintf(&sc_connect.output, _("connected to %s on port %d"), config.server_address,
99 config.server_port);
100 sc_connect = mp_set_subcheck_state(sc_connect, STATE_OK);
101 mp_add_subcheck_to_check(&overall, sc_connect);
102
85 /* Part I - Server Check */ 103 /* Part I - Server Check */
104 mp_subcheck sc_send = mp_subcheck_init();
86 105
87 /* send the OPTIONS request */ 106 /* send the OPTIONS request */
88 char buffer[MAX_INPUT_BUFFER]; 107 char buffer[MAX_INPUT_BUFFER];
89 sprintf(buffer, "OPTIONS rtsp://%s:%d RTSP/1.0\r\n", config.host_name, config.server_port); 108 sprintf(buffer, "OPTIONS rtsp://%s:%d RTSP/1.0\r\n", config.host_name, config.server_port);
90 ssize_t sent_bytes = send(socket, buffer, strlen(buffer), 0); 109 ssize_t sent_bytes = send(socket, buffer, strlen(buffer), 0);
91 if (sent_bytes == -1) { 110 if (sent_bytes == -1) {
92 die(STATE_CRITICAL, _("Sending options to %s failed\n"), config.host_name); 111 xasprintf(&sc_send.output, _("Sending options to %s failed"), config.host_name);
112 sc_send = mp_set_subcheck_state(sc_send, STATE_CRITICAL);
113 mp_add_subcheck_to_check(&overall, sc_send);
114 mp_exit(overall);
93 } 115 }
94 116
95 /* send the header sync */ 117 /* send the header sync */
96 sprintf(buffer, "CSeq: 1\r\n"); 118 sprintf(buffer, "CSeq: 1\r\n");
97 sent_bytes = send(socket, buffer, strlen(buffer), 0); 119 sent_bytes = send(socket, buffer, strlen(buffer), 0);
98 if (sent_bytes == -1) { 120 if (sent_bytes == -1) {
99 die(STATE_CRITICAL, _("Sending header sync to %s failed\n"), config.host_name); 121 xasprintf(&sc_send.output, _("Sending header sync to %s failed"), config.host_name);
122 sc_send = mp_set_subcheck_state(sc_send, STATE_CRITICAL);
123 mp_add_subcheck_to_check(&overall, sc_send);
124 mp_exit(overall);
100 } 125 }
101 126
102 /* send a newline so the server knows we're done with the request */ 127 /* send a newline so the server knows we're done with the request */
103 sprintf(buffer, "\r\n"); 128 sprintf(buffer, "\r\n");
104 sent_bytes = send(socket, buffer, strlen(buffer), 0); 129 sent_bytes = send(socket, buffer, strlen(buffer), 0);
105 if (sent_bytes == -1) { 130 if (sent_bytes == -1) {
106 die(STATE_CRITICAL, _("Sending newline to %s failed\n"), config.host_name); 131 xasprintf(&sc_send.output, _("Sending newline to %s failed"), config.host_name);
132 sc_send = mp_set_subcheck_state(sc_send, STATE_CRITICAL);
133 mp_add_subcheck_to_check(&overall, sc_send);
134 mp_exit(overall);
107 } 135 }
108 136
109 /* watch for the REAL connection string */ 137 /* watch for the REAL connection string */
@@ -111,60 +139,75 @@ int main(int argc, char **argv) {
111 139
112 /* return a CRITICAL status if we couldn't read any data */ 140 /* return a CRITICAL status if we couldn't read any data */
113 if (received_bytes == -1) { 141 if (received_bytes == -1) {
114 die(STATE_CRITICAL, _("No data received from %s\n"), config.host_name); 142 xasprintf(&sc_send.output, _("No data received from %s"), config.host_name);
143 sc_send = mp_set_subcheck_state(sc_send, STATE_CRITICAL);
144 mp_add_subcheck_to_check(&overall, sc_send);
145 mp_exit(overall);
115 } 146 }
116 147
117 mp_state_enum result = STATE_OK; 148 time_t end_time;
118 char *status_line = NULL; 149 {
119 /* make sure we find the response we are looking for */ 150 mp_subcheck sc_options_request = mp_subcheck_init();
120 if (!strstr(buffer, config.server_expect)) { 151 mp_state_enum options_result = STATE_OK;
121 if (config.server_port == PORT) { 152 /* make sure we find the response we are looking for */
122 printf("%s\n", _("Invalid REAL response received from host")); 153 if (!strstr(buffer, config.server_expect)) {
154 if (config.server_port == PORT) {
155 xasprintf(&sc_options_request.output, "invalid REAL response received from host");
156 } else {
157 xasprintf(&sc_options_request.output,
158 "invalid REAL response received from host on port %d",
159 config.server_port);
160 }
123 } else { 161 } else {
124 printf(_("Invalid REAL response received from host on port %d\n"), config.server_port); 162 /* else we got the REAL string, so check the return code */
125 } 163 time(&end_time);
126 } else {
127 /* else we got the REAL string, so check the return code */
128 164
129 time(&end_time); 165 options_result = STATE_OK;
130 166
131 result = STATE_OK; 167 char *status_line = strtok(buffer, "\n");
168 xasprintf(&sc_options_request.output, "status line: %s", status_line);
132 169
133 status_line = strtok(buffer, "\n"); 170 if (strstr(status_line, "200")) {
134 171 options_result = STATE_OK;
135 if (strstr(status_line, "200")) { 172 }
136 result = STATE_OK; 173 /* client errors options_result in a warning state */
174 else if (strstr(status_line, "400")) {
175 options_result = STATE_WARNING;
176 } else if (strstr(status_line, "401")) {
177 options_result = STATE_WARNING;
178 } else if (strstr(status_line, "402")) {
179 options_result = STATE_WARNING;
180 } else if (strstr(status_line, "403")) {
181 options_result = STATE_WARNING;
182 } else if (strstr(status_line, "404")) {
183 options_result = STATE_WARNING;
184 } else if (strstr(status_line, "500")) {
185 /* server errors options_result in a critical state */
186 options_result = STATE_CRITICAL;
187 } else if (strstr(status_line, "501")) {
188 options_result = STATE_CRITICAL;
189 } else if (strstr(status_line, "502")) {
190 options_result = STATE_CRITICAL;
191 } else if (strstr(status_line, "503")) {
192 options_result = STATE_CRITICAL;
193 } else {
194 options_result = STATE_UNKNOWN;
195 }
137 } 196 }
138 197
139 /* client errors result in a warning state */ 198 sc_options_request = mp_set_subcheck_state(sc_options_request, options_result);
140 else if (strstr(status_line, "400")) { 199 mp_add_subcheck_to_check(&overall, sc_options_request);
141 result = STATE_WARNING; 200
142 } else if (strstr(status_line, "401")) { 201 if (options_result != STATE_OK) {
143 result = STATE_WARNING; 202 // exit here if Setting options already failed
144 } else if (strstr(status_line, "402")) { 203 mp_exit(overall);
145 result = STATE_WARNING;
146 } else if (strstr(status_line, "403")) {
147 result = STATE_WARNING;
148 } else if (strstr(status_line, "404")) {
149 result = STATE_WARNING;
150 } else if (strstr(status_line, "500")) {
151 /* server errors result in a critical state */
152 result = STATE_CRITICAL;
153 } else if (strstr(status_line, "501")) {
154 result = STATE_CRITICAL;
155 } else if (strstr(status_line, "502")) {
156 result = STATE_CRITICAL;
157 } else if (strstr(status_line, "503")) {
158 result = STATE_CRITICAL;
159 } else {
160 result = STATE_UNKNOWN;
161 } 204 }
162 } 205 }
163 206
164 /* Part II - Check stream exists and is ok */ 207 /* Part II - Check stream exists and is ok */
165 if ((result == STATE_OK) && (config.server_url != NULL)) { 208 if (config.server_url != NULL) {
166
167 /* Part I - Server Check */ 209 /* Part I - Server Check */
210 mp_subcheck sc_describe = mp_subcheck_init();
168 211
169 /* send the DESCRIBE request */ 212 /* send the DESCRIBE request */
170 sprintf(buffer, "DESCRIBE rtsp://%s:%d%s RTSP/1.0\r\n", config.host_name, 213 sprintf(buffer, "DESCRIBE rtsp://%s:%d%s RTSP/1.0\r\n", config.host_name,
@@ -172,98 +215,115 @@ int main(int argc, char **argv) {
172 215
173 ssize_t sent_bytes = send(socket, buffer, strlen(buffer), 0); 216 ssize_t sent_bytes = send(socket, buffer, strlen(buffer), 0);
174 if (sent_bytes == -1) { 217 if (sent_bytes == -1) {
175 die(STATE_CRITICAL, _("Sending DESCRIBE request to %s failed\n"), config.host_name); 218 sc_describe = mp_set_subcheck_state(sc_describe, STATE_CRITICAL);
219 xasprintf(&sc_describe.output, "sending DESCRIBE request to %s failed",
220 config.host_name);
221 mp_add_subcheck_to_check(&overall, sc_describe);
222 mp_exit(overall);
176 } 223 }
177 224
178 /* send the header sync */ 225 /* send the header sync */
179 sprintf(buffer, "CSeq: 2\r\n"); 226 sprintf(buffer, "CSeq: 2\r\n");
180 sent_bytes = send(socket, buffer, strlen(buffer), 0); 227 sent_bytes = send(socket, buffer, strlen(buffer), 0);
181 if (sent_bytes == -1) { 228 if (sent_bytes == -1) {
182 die(STATE_CRITICAL, _("Sending DESCRIBE request to %s failed\n"), config.host_name); 229 sc_describe = mp_set_subcheck_state(sc_describe, STATE_CRITICAL);
230 xasprintf(&sc_describe.output, "sending DESCRIBE request to %s failed",
231 config.host_name);
232 mp_add_subcheck_to_check(&overall, sc_describe);
233 mp_exit(overall);
183 } 234 }
184 235
185 /* send a newline so the server knows we're done with the request */ 236 /* send a newline so the server knows we're done with the request */
186 sprintf(buffer, "\r\n"); 237 sprintf(buffer, "\r\n");
187 sent_bytes = send(socket, buffer, strlen(buffer), 0); 238 sent_bytes = send(socket, buffer, strlen(buffer), 0);
188 if (sent_bytes == -1) { 239 if (sent_bytes == -1) {
189 die(STATE_CRITICAL, _("Sending DESCRIBE request to %s failed\n"), config.host_name); 240 sc_describe = mp_set_subcheck_state(sc_describe, STATE_CRITICAL);
241 xasprintf(&sc_describe.output, "sending DESCRIBE request to %s failed",
242 config.host_name);
243 mp_add_subcheck_to_check(&overall, sc_describe);
244 mp_exit(overall);
190 } 245 }
191 246
192 /* watch for the REAL connection string */ 247 /* watch for the REAL connection string */
193 ssize_t recv_bytes = recv(socket, buffer, MAX_INPUT_BUFFER - 1, 0); 248 ssize_t recv_bytes = recv(socket, buffer, MAX_INPUT_BUFFER - 1, 0);
194 if (recv_bytes == -1) { 249 if (recv_bytes == -1) {
195 /* return a CRITICAL status if we couldn't read any data */ 250 /* return a CRITICAL status if we couldn't read any data */
196 printf(_("No data received from host\n")); 251 sc_describe = mp_set_subcheck_state(sc_describe, STATE_CRITICAL);
197 result = STATE_CRITICAL; 252 xasprintf(&sc_describe.output, "No data received from host on DESCRIBE request");
253 mp_add_subcheck_to_check(&overall, sc_describe);
254 mp_exit(overall);
198 } else { 255 } else {
199 buffer[result] = '\0'; /* null terminate received buffer */ 256 buffer[recv_bytes] = '\0'; /* null terminate received buffer */
200 /* make sure we find the response we are looking for */ 257 /* make sure we find the response we are looking for */
201 if (!strstr(buffer, config.server_expect)) { 258 if (!strstr(buffer, config.server_expect)) {
202 if (config.server_port == PORT) { 259 if (config.server_port == PORT) {
203 printf("%s\n", _("Invalid REAL response received from host")); 260 xasprintf(&sc_describe.output, "invalid REAL response received from host");
204 } else { 261 } else {
205 printf(_("Invalid REAL response received from host on port %d\n"), 262 xasprintf(&sc_describe.output,
206 config.server_port); 263 "invalid REAL response received from host on port %d",
264 config.server_port);
207 } 265 }
208 } else {
209 266
267 sc_describe = mp_set_subcheck_state(sc_describe, STATE_UNKNOWN);
268 mp_add_subcheck_to_check(&overall, sc_describe);
269 mp_exit(overall);
270 } else {
210 /* else we got the REAL string, so check the return code */ 271 /* else we got the REAL string, so check the return code */
211 272
212 time(&end_time); 273 time(&end_time);
213 274
214 result = STATE_OK; 275 char *status_line = strtok(buffer, "\n");
215 276 xasprintf(&sc_describe.output, "status line: %s", status_line);
216 status_line = strtok(buffer, "\n");
217 277
278 mp_state_enum describe_result;
218 if (strstr(status_line, "200")) { 279 if (strstr(status_line, "200")) {
219 result = STATE_OK; 280 describe_result = STATE_OK;
220 } 281 }
221 282 /* client errors describe_result in a warning state */
222 /* client errors result in a warning state */
223 else if (strstr(status_line, "400")) { 283 else if (strstr(status_line, "400")) {
224 result = STATE_WARNING; 284 describe_result = STATE_WARNING;
225 } else if (strstr(status_line, "401")) { 285 } else if (strstr(status_line, "401")) {
226 result = STATE_WARNING; 286 describe_result = STATE_WARNING;
227 } else if (strstr(status_line, "402")) { 287 } else if (strstr(status_line, "402")) {
228 result = STATE_WARNING; 288 describe_result = STATE_WARNING;
229 } else if (strstr(status_line, "403")) { 289 } else if (strstr(status_line, "403")) {
230 result = STATE_WARNING; 290 describe_result = STATE_WARNING;
231 } else if (strstr(status_line, "404")) { 291 } else if (strstr(status_line, "404")) {
232 result = STATE_WARNING; 292 describe_result = STATE_WARNING;
233 } 293 }
234 294 /* server errors describe_result in a critical state */
235 /* server errors result in a critical state */
236 else if (strstr(status_line, "500")) { 295 else if (strstr(status_line, "500")) {
237 result = STATE_CRITICAL; 296 describe_result = STATE_CRITICAL;
238 } else if (strstr(status_line, "501")) { 297 } else if (strstr(status_line, "501")) {
239 result = STATE_CRITICAL; 298 describe_result = STATE_CRITICAL;
240 } else if (strstr(status_line, "502")) { 299 } else if (strstr(status_line, "502")) {
241 result = STATE_CRITICAL; 300 describe_result = STATE_CRITICAL;
242 } else if (strstr(status_line, "503")) { 301 } else if (strstr(status_line, "503")) {
243 result = STATE_CRITICAL; 302 describe_result = STATE_CRITICAL;
303 } else {
304 describe_result = STATE_UNKNOWN;
244 } 305 }
245 306
246 else { 307 sc_describe = mp_set_subcheck_state(sc_describe, describe_result);
247 result = STATE_UNKNOWN; 308 mp_add_subcheck_to_check(&overall, sc_describe);
248 }
249 } 309 }
250 } 310 }
251 } 311 }
252 312
253 /* Return results */ 313 /* Return results */
254 if (result == STATE_OK) { 314 mp_subcheck sc_timing = mp_subcheck_init();
255 if (config.check_critical_time && (end_time - start_time) > config.critical_time) { 315 xasprintf(&sc_timing.output, "response time: %lds", end_time - start_time);
256 result = STATE_CRITICAL; 316 sc_timing = mp_set_subcheck_default_state(sc_timing, STATE_OK);
257 } else if (config.check_warning_time && (end_time - start_time) > config.warning_time) {
258 result = STATE_WARNING;
259 }
260 317
261 /* Put some HTML in here to create a dynamic link */ 318 mp_perfdata pd_response_time = perfdata_init();
262 printf(_("REAL %s - %d second response time\n"), state_text(result), 319 pd_response_time = mp_set_pd_value(pd_response_time, (end_time - start_time));
263 (int)(end_time - start_time)); 320 pd_response_time.label = "response_time";
264 } else { 321 pd_response_time.uom = "s";
265 printf("%s\n", status_line); 322 pd_response_time = mp_pd_set_thresholds(pd_response_time, config.time_thresholds);
266 } 323 mp_add_perfdata_to_subcheck(&sc_connect, pd_response_time);
324 sc_timing = mp_set_subcheck_state(sc_timing, mp_get_pd_status(pd_response_time));
325
326 mp_add_subcheck_to_check(&overall, sc_timing);
267 327
268 /* close the connection */ 328 /* close the connection */
269 close(socket); 329 close(socket);
@@ -271,18 +331,28 @@ int main(int argc, char **argv) {
271 /* reset the alarm */ 331 /* reset the alarm */
272 alarm(0); 332 alarm(0);
273 333
274 exit(result); 334 mp_exit(overall);
275} 335}
276 336
277/* process command-line arguments */ 337/* process command-line arguments */
278check_real_config_wrapper process_arguments(int argc, char **argv) { 338check_real_config_wrapper process_arguments(int argc, char **argv) {
279 static struct option longopts[] = { 339 enum {
280 {"hostname", required_argument, 0, 'H'}, {"IPaddress", required_argument, 0, 'I'}, 340 output_format_index = CHAR_MAX + 1,
281 {"expect", required_argument, 0, 'e'}, {"url", required_argument, 0, 'u'}, 341 };
282 {"port", required_argument, 0, 'p'}, {"critical", required_argument, 0, 'c'}, 342
283 {"warning", required_argument, 0, 'w'}, {"timeout", required_argument, 0, 't'}, 343 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
284 {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, 344 {"IPaddress", required_argument, 0, 'I'},
285 {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; 345 {"expect", required_argument, 0, 'e'},
346 {"url", required_argument, 0, 'u'},
347 {"port", required_argument, 0, 'p'},
348 {"critical", required_argument, 0, 'c'},
349 {"warning", required_argument, 0, 'w'},
350 {"timeout", required_argument, 0, 't'},
351 {"verbose", no_argument, 0, 'v'},
352 {"version", no_argument, 0, 'V'},
353 {"help", no_argument, 0, 'h'},
354 {"output-format", required_argument, 0, output_format_index},
355 {0, 0, 0, 0}};
286 356
287 check_real_config_wrapper result = { 357 check_real_config_wrapper result = {
288 .errorcode = OK, 358 .errorcode = OK,
@@ -337,21 +407,23 @@ check_real_config_wrapper process_arguments(int argc, char **argv) {
337 } 407 }
338 break; 408 break;
339 case 'w': /* warning time threshold */ 409 case 'w': /* warning time threshold */
340 if (is_intnonneg(optarg)) { 410 {
341 result.config.warning_time = atoi(optarg); 411 mp_range_parsed critical_range = mp_parse_range_string(optarg);
342 result.config.check_warning_time = true; 412 if (critical_range.error != MP_PARSING_SUCCESS) {
343 } else { 413 die(STATE_UNKNOWN, "failed to parse warning threshold: %s", optarg);
344 usage4(_("Warning time must be a positive integer"));
345 } 414 }
346 break; 415 result.config.time_thresholds =
416 mp_thresholds_set_warn(result.config.time_thresholds, critical_range.range);
417 } break;
347 case 'c': /* critical time threshold */ 418 case 'c': /* critical time threshold */
348 if (is_intnonneg(optarg)) { 419 {
349 result.config.critical_time = atoi(optarg); 420 mp_range_parsed critical_range = mp_parse_range_string(optarg);
350 result.config.check_critical_time = true; 421 if (critical_range.error != MP_PARSING_SUCCESS) {
351 } else { 422 die(STATE_UNKNOWN, "failed to parse critical threshold: %s", optarg);
352 usage4(_("Critical time must be a positive integer"));
353 } 423 }
354 break; 424 result.config.time_thresholds =
425 mp_thresholds_set_crit(result.config.time_thresholds, critical_range.range);
426 } break;
355 case 'v': /* verbose */ 427 case 'v': /* verbose */
356 verbose = true; 428 verbose = true;
357 break; 429 break;
@@ -368,6 +440,18 @@ check_real_config_wrapper process_arguments(int argc, char **argv) {
368 case 'h': /* help */ 440 case 'h': /* help */
369 print_help(); 441 print_help();
370 exit(STATE_UNKNOWN); 442 exit(STATE_UNKNOWN);
443 case output_format_index: {
444 parsed_output_format parser = mp_parse_output_format(optarg);
445 if (!parser.parsing_success) {
446 // TODO List all available formats here, maybe add anothoer usage function
447 printf("Invalid output format: %s\n", optarg);
448 exit(STATE_UNKNOWN);
449 }
450
451 result.config.output_format_is_set = true;
452 result.config.output_format = parser.output_format;
453 break;
454 }
371 case '?': /* usage */ 455 case '?': /* usage */
372 usage5(); 456 usage5();
373 } 457 }
@@ -390,10 +474,6 @@ check_real_config_wrapper process_arguments(int argc, char **argv) {
390 result.config.host_name = strdup(result.config.server_address); 474 result.config.host_name = strdup(result.config.server_address);
391 } 475 }
392 476
393 if (result.config.server_expect == NULL) {
394 result.config.server_expect = strdup(EXPECT);
395 }
396
397 return result; 477 return result;
398} 478}
399 479
@@ -420,7 +500,7 @@ void print_help(void) {
420 printf(" %s\n", "-u, --url=STRING"); 500 printf(" %s\n", "-u, --url=STRING");
421 printf(" %s\n", _("Connect to this url")); 501 printf(" %s\n", _("Connect to this url"));
422 printf(" %s\n", "-e, --expect=STRING"); 502 printf(" %s\n", "-e, --expect=STRING");
423 printf(_("String to expect in first line of server response (default: %s)\n"), EXPECT); 503 printf(_("String to expect in first line of server response (default: %s)\n"), default_expect);
424 504
425 printf(UT_WARN_CRIT); 505 printf(UT_WARN_CRIT);
426 506
diff --git a/plugins/check_real.d/config.h b/plugins/check_real.d/config.h
index c4663cf9..2d99ad49 100644
--- a/plugins/check_real.d/config.h
+++ b/plugins/check_real.d/config.h
@@ -1,12 +1,16 @@
1#pragma once 1#pragma once
2 2
3#include "../../config.h" 3#include "../../config.h"
4#include "output.h"
5#include "thresholds.h"
4#include <stddef.h> 6#include <stddef.h>
5 7
6enum { 8enum {
7 PORT = 554 9 PORT = 554
8}; 10};
9 11
12const char *default_expect = "RTSP/1.";
13
10typedef struct { 14typedef struct {
11 char *server_address; 15 char *server_address;
12 char *host_name; 16 char *host_name;
@@ -14,10 +18,11 @@ typedef struct {
14 char *server_url; 18 char *server_url;
15 19
16 char *server_expect; 20 char *server_expect;
17 int warning_time; 21
18 bool check_warning_time; 22 mp_thresholds time_thresholds;
19 int critical_time; 23
20 bool check_critical_time; 24 bool output_format_is_set;
25 mp_output_format output_format;
21} check_real_config; 26} check_real_config;
22 27
23check_real_config check_real_config_init() { 28check_real_config check_real_config_init() {
@@ -27,11 +32,11 @@ check_real_config check_real_config_init() {
27 .server_port = PORT, 32 .server_port = PORT,
28 .server_url = NULL, 33 .server_url = NULL,
29 34
30 .server_expect = NULL, 35 .server_expect = default_expect,
31 .warning_time = 0, 36
32 .check_warning_time = false, 37 .time_thresholds = mp_thresholds_init(),
33 .critical_time = 0, 38
34 .check_critical_time = false, 39 .output_format_is_set = false,
35 }; 40 };
36 return tmp; 41 return tmp;
37} 42}
diff --git a/plugins/check_smtp.c b/plugins/check_smtp.c
index e806ad29..24883fd8 100644
--- a/plugins/check_smtp.c
+++ b/plugins/check_smtp.c
@@ -37,7 +37,6 @@
37#include "base64.h" 37#include "base64.h"
38#include "regex.h" 38#include "regex.h"
39 39
40#include <bits/getopt_ext.h>
41#include <ctype.h> 40#include <ctype.h>
42#include <string.h> 41#include <string.h>
43#include "check_smtp.d/config.h" 42#include "check_smtp.d/config.h"
@@ -101,6 +100,14 @@ static int my_close(int /*socket_descriptor*/);
101static int verbose = 0; 100static int verbose = 0;
102 101
103int main(int argc, char **argv) { 102int main(int argc, char **argv) {
103#ifdef __OpenBSD__
104 /* - rpath is required to read --extra-opts (given up later)
105 * - inet is required for sockets
106 * - unix is required for Unix domain sockets
107 * - dns is required for name lookups */
108 pledge("stdio rpath inet unix dns", NULL);
109#endif // __OpenBSD__
110
104 setlocale(LC_ALL, ""); 111 setlocale(LC_ALL, "");
105 bindtextdomain(PACKAGE, LOCALEDIR); 112 bindtextdomain(PACKAGE, LOCALEDIR);
106 textdomain(PACKAGE); 113 textdomain(PACKAGE);
@@ -114,6 +121,10 @@ int main(int argc, char **argv) {
114 usage4(_("Could not parse arguments")); 121 usage4(_("Could not parse arguments"));
115 } 122 }
116 123
124#ifdef __OpenBSD__
125 pledge("stdio inet unix dns", NULL);
126#endif // __OpenBSD__
127
117 const check_smtp_config config = tmp_config.config; 128 const check_smtp_config config = tmp_config.config;
118 129
119 if (config.output_format_is_set) { 130 if (config.output_format_is_set) {
@@ -724,7 +735,7 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
724 break; 735 break;
725 case 'c': /* critical time threshold */ { 736 case 'c': /* critical time threshold */ {
726 mp_range_parsed tmp = mp_parse_range_string(optarg); 737 mp_range_parsed tmp = mp_parse_range_string(optarg);
727 if (tmp.error != MP_PARSING_SUCCES) { 738 if (tmp.error != MP_PARSING_SUCCESS) {
728 die(STATE_UNKNOWN, "failed to parse critical time threshold"); 739 die(STATE_UNKNOWN, "failed to parse critical time threshold");
729 } 740 }
730 result.config.connection_time = 741 result.config.connection_time =
@@ -732,7 +743,7 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
732 } break; 743 } break;
733 case 'w': /* warning time threshold */ { 744 case 'w': /* warning time threshold */ {
734 mp_range_parsed tmp = mp_parse_range_string(optarg); 745 mp_range_parsed tmp = mp_parse_range_string(optarg);
735 if (tmp.error != MP_PARSING_SUCCES) { 746 if (tmp.error != MP_PARSING_SUCCESS) {
736 die(STATE_UNKNOWN, "failed to parse warning time threshold"); 747 die(STATE_UNKNOWN, "failed to parse warning time threshold");
737 } 748 }
738 result.config.connection_time = 749 result.config.connection_time =
@@ -808,11 +819,7 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
808 address_family = AF_INET; 819 address_family = AF_INET;
809 break; 820 break;
810 case '6': 821 case '6':
811#ifdef USE_IPV6
812 address_family = AF_INET6; 822 address_family = AF_INET6;
813#else
814 usage4(_("IPv6 support not available"));
815#endif
816 break; 823 break;
817 case 'V': /* version */ 824 case 'V': /* version */
818 print_revision(progname, NP_VERSION); 825 print_revision(progname, NP_VERSION);
diff --git a/plugins/check_snmp.d/check_snmp_helpers.c b/plugins/check_snmp.d/check_snmp_helpers.c
index f506537a..83e94a34 100644
--- a/plugins/check_snmp.d/check_snmp_helpers.c
+++ b/plugins/check_snmp.d/check_snmp_helpers.c
@@ -36,7 +36,8 @@ int check_snmp_set_thresholds(const char *threshold_string, check_snmp_test_unit
36 threshold_string++; 36 threshold_string++;
37 } 37 }
38 38
39 for (char *ptr = strtok(threshold_string, ", "); ptr != NULL; 39 char *thr_string_copy = strdup(threshold_string);
40 for (char *ptr = strtok(thr_string_copy, ", "); ptr != NULL;
40 ptr = strtok(NULL, ", "), tu_index++) { 41 ptr = strtok(NULL, ", "), tu_index++) {
41 42
42 if (tu_index > max_test_units) { 43 if (tu_index > max_test_units) {
@@ -51,7 +52,7 @@ int check_snmp_set_thresholds(const char *threshold_string, check_snmp_test_unit
51 } 52 }
52 53
53 mp_range_parsed tmp = mp_parse_range_string(ptr); 54 mp_range_parsed tmp = mp_parse_range_string(ptr);
54 if (tmp.error != MP_PARSING_SUCCES) { 55 if (tmp.error != MP_PARSING_SUCCESS) {
55 die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", ptr); 56 die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", ptr);
56 } 57 }
57 58
@@ -64,11 +65,12 @@ int check_snmp_set_thresholds(const char *threshold_string, check_snmp_test_unit
64 } 65 }
65 } 66 }
66 67
68 free(thr_string_copy);
67 } else { 69 } else {
68 // Single value 70 // Single value
69 // only valid for the first test unit 71 // only valid for the first test unit
70 mp_range_parsed tmp = mp_parse_range_string(threshold_string); 72 mp_range_parsed tmp = mp_parse_range_string(threshold_string);
71 if (tmp.error != MP_PARSING_SUCCES) { 73 if (tmp.error != MP_PARSING_SUCCESS) {
72 die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", threshold_string); 74 die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", threshold_string);
73 } 75 }
74 76
@@ -843,8 +845,8 @@ char *_np_state_calculate_location_prefix(void) {
843 * Sets variables. Generates filename. Returns np_state_key. die with 845 * Sets variables. Generates filename. Returns np_state_key. die with
844 * UNKNOWN if exception 846 * UNKNOWN if exception
845 */ 847 */
846state_key np_enable_state(char *keyname, int expected_data_version, char *plugin_name, int argc, 848state_key np_enable_state(char *keyname, int expected_data_version, const char *plugin_name,
847 char **argv) { 849 int argc, char **argv) {
848 state_key *this_state = (state_key *)calloc(1, sizeof(state_key)); 850 state_key *this_state = (state_key *)calloc(1, sizeof(state_key));
849 if (this_state == NULL) { 851 if (this_state == NULL) {
850 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); 852 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
@@ -869,7 +871,7 @@ state_key np_enable_state(char *keyname, int expected_data_version, char *plugin
869 tmp_char++; 871 tmp_char++;
870 } 872 }
871 this_state->name = temp_keyname; 873 this_state->name = temp_keyname;
872 this_state->plugin_name = plugin_name; 874 this_state->plugin_name = (char *)plugin_name;
873 this_state->data_version = expected_data_version; 875 this_state->data_version = expected_data_version;
874 this_state->state_data = NULL; 876 this_state->state_data = NULL;
875 877
diff --git a/plugins/check_snmp.d/check_snmp_helpers.h b/plugins/check_snmp.d/check_snmp_helpers.h
index 0f7780b1..95b361ac 100644
--- a/plugins/check_snmp.d/check_snmp_helpers.h
+++ b/plugins/check_snmp.d/check_snmp_helpers.h
@@ -66,6 +66,6 @@ typedef struct state_key_struct {
66} state_key; 66} state_key;
67 67
68state_data *np_state_read(state_key stateKey); 68state_data *np_state_read(state_key stateKey);
69state_key np_enable_state(char *keyname, int expected_data_version, char *plugin_name, int argc, 69state_key np_enable_state(char *keyname, int expected_data_version, const char *plugin_name,
70 char **argv); 70 int argc, char **argv);
71void np_state_write_string(state_key stateKey, time_t timestamp, char *stringToStore); 71void np_state_write_string(state_key stateKey, time_t timestamp, char *stringToStore);
diff --git a/plugins/check_ssh.c b/plugins/check_ssh.c
index f6c8d551..911f6787 100644
--- a/plugins/check_ssh.c
+++ b/plugins/check_ssh.c
@@ -61,6 +61,14 @@ static int ssh_connect(mp_check *overall, char *haddr, int hport, char *remote_v
61 char *remote_protocol); 61 char *remote_protocol);
62 62
63int main(int argc, char **argv) { 63int main(int argc, char **argv) {
64#ifdef __OpenBSD__
65 /* - rpath is required to read --extra-opts (given up later)
66 * - inet is required for sockets
67 * - unix is required for Unix domain sockets
68 * - dns is required for name lookups */
69 pledge("stdio rpath inet unix dns", NULL);
70#endif // __OpenBSD__
71
64 setlocale(LC_ALL, ""); 72 setlocale(LC_ALL, "");
65 bindtextdomain(PACKAGE, LOCALEDIR); 73 bindtextdomain(PACKAGE, LOCALEDIR);
66 textdomain(PACKAGE); 74 textdomain(PACKAGE);
@@ -74,6 +82,10 @@ int main(int argc, char **argv) {
74 usage4(_("Could not parse arguments")); 82 usage4(_("Could not parse arguments"));
75 } 83 }
76 84
85#ifdef __OpenBSD__
86 pledge("stdio inet unix dns", NULL);
87#endif // __OpenBSD__
88
77 check_ssh_config config = tmp_config.config; 89 check_ssh_config config = tmp_config.config;
78 90
79 mp_check overall = mp_check_init(); 91 mp_check overall = mp_check_init();
@@ -161,11 +173,7 @@ process_arguments_wrapper process_arguments(int argc, char **argv) {
161 address_family = AF_INET; 173 address_family = AF_INET;
162 break; 174 break;
163 case '6': 175 case '6':
164#ifdef USE_IPV6
165 address_family = AF_INET6; 176 address_family = AF_INET6;
166#else
167 usage4(_("IPv6 support not available"));
168#endif
169 break; 177 break;
170 case 'r': /* remote version */ 178 case 'r': /* remote version */
171 result.config.remote_version = optarg; 179 result.config.remote_version = optarg;
diff --git a/plugins/check_tcp.c b/plugins/check_tcp.c
index 09806373..49a8c4c1 100644
--- a/plugins/check_tcp.c
+++ b/plugins/check_tcp.c
@@ -89,6 +89,14 @@ const int DEFAULT_NNTPS_PORT = 563;
89const int DEFAULT_CLAMD_PORT = 3310; 89const int DEFAULT_CLAMD_PORT = 3310;
90 90
91int main(int argc, char **argv) { 91int main(int argc, char **argv) {
92#ifdef __OpenBSD__
93 /* - rpath is required to read --extra-opts (given up later)
94 * - inet is required for sockets
95 * - unix is required for Unix domain sockets
96 * - dns is required for name lookups */
97 pledge("stdio rpath inet unix dns", NULL);
98#endif // __OpenBSD__
99
92 setlocale(LC_ALL, ""); 100 setlocale(LC_ALL, "");
93 bindtextdomain(PACKAGE, LOCALEDIR); 101 bindtextdomain(PACKAGE, LOCALEDIR);
94 textdomain(PACKAGE); 102 textdomain(PACKAGE);
@@ -216,6 +224,10 @@ int main(int argc, char **argv) {
216 usage4(_("Could not parse arguments")); 224 usage4(_("Could not parse arguments"));
217 } 225 }
218 226
227#ifdef __OpenBSD__
228 pledge("stdio inet unix dns", NULL);
229#endif // __OpenBSD__
230
219 config = paw.config; 231 config = paw.config;
220 232
221 if (verbosity > 0) { 233 if (verbosity > 0) {
@@ -571,11 +583,7 @@ static check_tcp_config_wrapper process_arguments(int argc, char **argv, check_t
571 address_family = AF_INET; 583 address_family = AF_INET;
572 break; 584 break;
573 case '6': // Apparently unused TODO 585 case '6': // Apparently unused TODO
574#ifdef USE_IPV6
575 address_family = AF_INET6; 586 address_family = AF_INET6;
576#else
577 usage4(_("IPv6 support not available"));
578#endif
579 break; 587 break;
580 case 'H': /* hostname */ 588 case 'H': /* hostname */
581 config.host_specified = true; 589 config.host_specified = true;
diff --git a/plugins/check_time.c b/plugins/check_time.c
index fc9ba3f9..99708ad3 100644
--- a/plugins/check_time.c
+++ b/plugins/check_time.c
@@ -68,6 +68,7 @@ int main(int argc, char **argv) {
68 68
69 /* set socket timeout */ 69 /* set socket timeout */
70 alarm(socket_timeout); 70 alarm(socket_timeout);
71 time_t start_time;
71 time(&start_time); 72 time(&start_time);
72 73
73 int socket; 74 int socket;
@@ -113,6 +114,7 @@ int main(int argc, char **argv) {
113 close(socket); 114 close(socket);
114 115
115 /* reset the alarm */ 116 /* reset the alarm */
117 time_t end_time;
116 time(&end_time); 118 time(&end_time);
117 alarm(0); 119 alarm(0);
118 120
diff --git a/plugins/check_users.c b/plugins/check_users.c
index 3b2e265e..4027d21a 100644
--- a/plugins/check_users.c
+++ b/plugins/check_users.c
@@ -222,7 +222,7 @@ check_users_config_wrapper process_arguments(int argc, char **argv) {
222 exit(STATE_UNKNOWN); 222 exit(STATE_UNKNOWN);
223 } 223 }
224 224
225 if (tmp.error == MP_PARSING_SUCCES) { 225 if (tmp.error == MP_PARSING_SUCCESS) {
226 result.config.thresholds.warning = tmp.range; 226 result.config.thresholds.warning = tmp.range;
227 result.config.thresholds.warning_is_set = true; 227 result.config.thresholds.warning_is_set = true;
228 } else { 228 } else {
@@ -238,7 +238,7 @@ check_users_config_wrapper process_arguments(int argc, char **argv) {
238 exit(STATE_UNKNOWN); 238 exit(STATE_UNKNOWN);
239 } 239 }
240 240
241 if (tmp.error == MP_PARSING_SUCCES) { 241 if (tmp.error == MP_PARSING_SUCCESS) {
242 result.config.thresholds.critical = tmp.range; 242 result.config.thresholds.critical = tmp.range;
243 result.config.thresholds.critical_is_set = true; 243 result.config.thresholds.critical_is_set = true;
244 } else { 244 } else {
diff --git a/plugins/netutils.c b/plugins/netutils.c
index b4c6ff0a..f9933ebd 100644
--- a/plugins/netutils.c
+++ b/plugins/netutils.c
@@ -38,11 +38,7 @@ mp_state_enum socket_timeout_state = STATE_CRITICAL;
38mp_state_enum econn_refuse_state = STATE_CRITICAL; 38mp_state_enum econn_refuse_state = STATE_CRITICAL;
39bool was_refused = false; 39bool was_refused = false;
40 40
41#if USE_IPV6
42int address_family = AF_UNSPEC; 41int address_family = AF_UNSPEC;
43#else
44int address_family = AF_INET;
45#endif
46 42
47/* handles socket timeouts */ 43/* handles socket timeouts */
48void socket_timeout_alarm_handler(int sig) { 44void socket_timeout_alarm_handler(int sig) {
@@ -348,7 +344,6 @@ void host_or_die(const char *str) {
348} 344}
349 345
350bool is_addr(const char *address) { 346bool is_addr(const char *address) {
351#ifdef USE_IPV6
352 if (address_family == AF_INET && is_inet_addr(address)) { 347 if (address_family == AF_INET && is_inet_addr(address)) {
353 return true; 348 return true;
354 } 349 }
@@ -356,11 +351,6 @@ bool is_addr(const char *address) {
356 if (address_family == AF_INET6 && is_inet6_addr(address)) { 351 if (address_family == AF_INET6 && is_inet6_addr(address)) {
357 return true; 352 return true;
358 } 353 }
359#else
360 if (is_inet_addr(address)) {
361 return true;
362 }
363#endif
364 354
365 return false; 355 return false;
366} 356}
diff --git a/plugins/netutils.h b/plugins/netutils.h
index dbd22398..f3d046c3 100644
--- a/plugins/netutils.h
+++ b/plugins/netutils.h
@@ -78,12 +78,8 @@ bool dns_lookup(const char *, struct sockaddr_storage *, int);
78void host_or_die(const char *str); 78void host_or_die(const char *str);
79#define resolve_host_or_addr(addr, family) dns_lookup(addr, NULL, family) 79#define resolve_host_or_addr(addr, family) dns_lookup(addr, NULL, family)
80#define is_inet_addr(addr) resolve_host_or_addr(addr, AF_INET) 80#define is_inet_addr(addr) resolve_host_or_addr(addr, AF_INET)
81#ifdef USE_IPV6
82# define is_inet6_addr(addr) resolve_host_or_addr(addr, AF_INET6) 81# define is_inet6_addr(addr) resolve_host_or_addr(addr, AF_INET6)
83# define is_hostname(addr) resolve_host_or_addr(addr, address_family) 82# define is_hostname(addr) resolve_host_or_addr(addr, address_family)
84#else
85# define is_hostname(addr) resolve_host_or_addr(addr, AF_INET)
86#endif
87 83
88extern unsigned int socket_timeout; 84extern unsigned int socket_timeout;
89extern mp_state_enum socket_timeout_state; 85extern mp_state_enum socket_timeout_state;
diff --git a/plugins/t/check_curl.t b/plugins/t/check_curl.t
index 2c2fafde..a8326f12 100644
--- a/plugins/t/check_curl.t
+++ b/plugins/t/check_curl.t
@@ -13,7 +13,7 @@ use vars qw($tests $has_ipv6);
13BEGIN { 13BEGIN {
14 use NPTest; 14 use NPTest;
15 $has_ipv6 = NPTest::has_ipv6(); 15 $has_ipv6 = NPTest::has_ipv6();
16 $tests = $has_ipv6 ? 55 : 53; 16 $tests = $has_ipv6 ? 57 : 92;
17 plan tests => $tests; 17 plan tests => $tests;
18} 18}
19 19
@@ -25,7 +25,13 @@ my $plugin = 'check_http';
25$plugin = 'check_curl' if $0 =~ m/check_curl/mx; 25$plugin = 'check_curl' if $0 =~ m/check_curl/mx;
26 26
27my $host_tcp_http = getTestParameter("NP_HOST_TCP_HTTP", "A host providing the HTTP Service (a web server)", "localhost"); 27my $host_tcp_http = getTestParameter("NP_HOST_TCP_HTTP", "A host providing the HTTP Service (a web server)", "localhost");
28my $host_tcp_http_subdomain = getTestParameter("NP_HOST_TCP_HTTP_SUBDOMAIN", "A host that is served under a subdomain name", "subdomain1.localhost.com");
29my $host_tcp_http_ipv4 = getTestParameter("NP_HOST_TCP_HTTP_IPV4", "An IPv6 address providing a HTTP Service (a web server)", "127.0.0.1");
30my $host_tcp_http_ipv4_cidr_1 = getTestParameter("NP_HOST_TCP_HTTP_IPV4_CIDR_1", "A CIDR that the provided IPv4 address is in.");
31my $host_tcp_http_ipv4_cidr_2 = getTestParameter("NP_HOST_TCP_HTTP_IPV4_CIDR_2", "A CIDR that the provided IPv4 address is in.");
28my $host_tcp_http_ipv6 = getTestParameter("NP_HOST_TCP_HTTP_IPV6", "An IPv6 address providing a HTTP Service (a web server)", "::1"); 32my $host_tcp_http_ipv6 = getTestParameter("NP_HOST_TCP_HTTP_IPV6", "An IPv6 address providing a HTTP Service (a web server)", "::1");
33my $host_tcp_http_ipv6_cidr_1 = getTestParameter("NP_HOST_TCP_HTTP_IPV6_CIDR_1", "A CIDR that the provided IPv6 address is in.");
34my $host_tcp_http_ipv6_cidr_2 = getTestParameter("NP_HOST_TCP_HTTP_IPV6_CIDR_2", "A CIDR that the provided IPv6 address is in.");
29my $host_tls_http = getTestParameter("NP_HOST_TLS_HTTP", "A host providing the HTTPS Service (a tls web server)", "localhost"); 35my $host_tls_http = getTestParameter("NP_HOST_TLS_HTTP", "A host providing the HTTPS Service (a tls web server)", "localhost");
30my $host_tls_cert = getTestParameter("NP_HOST_TLS_CERT", "the common name of the certificate.", "localhost"); 36my $host_tls_cert = getTestParameter("NP_HOST_TLS_CERT", "the common name of the certificate.", "localhost");
31my $host_nonresponsive = getTestParameter("NP_HOST_NONRESPONSIVE", "The hostname of system not responsive to network requests", "10.0.0.1"); 37my $host_nonresponsive = getTestParameter("NP_HOST_NONRESPONSIVE", "The hostname of system not responsive to network requests", "10.0.0.1");
@@ -222,3 +228,110 @@ SKIP: {
222 $res = NPTest->testCmd( "./$plugin -H monitoring-plugins.org --extended-perfdata" ); 228 $res = NPTest->testCmd( "./$plugin -H monitoring-plugins.org --extended-perfdata" );
223 like ( $res->output, '/\'time_connect\'=[\d\.]+/', 'Extended Performance Data Output OK' ); 229 like ( $res->output, '/\'time_connect\'=[\d\.]+/', 'Extended Performance Data Output OK' );
224} 230}
231SKIP: {
232 skip "No internet access", 2 if $internet_access eq "no";
233
234 # Proxy tests
235 # These are the proxy tests that require a working proxy server
236 # The debian container in the github workflow runs a squid proxy server at port 3128
237 # Test that dont require one, like argument/environment variable parsing are in plugins/tests/check_curl.t
238
239 # Test if proxy works
240 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http --proxy http://$host_tcp_proxy:$port_tcp_proxy -v" );
241 like($res->output, qr/^\* proxy_resolves_hostname: 1/m, "proxy is used, there are no preventative measures ");
242 is( $res->return_code, 0, "Using proxy http://$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http works" );
243
244 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv4 --proxy http://$host_tcp_proxy:$port_tcp_proxy -v" );
245 like($res->output, qr/^\* proxy_resolves_hostname: 1/m, "proxy is used, there are no preventative measures ");
246 is( $res->return_code, 0, "Using proxy http://$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http_ipv4 works" );
247
248 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv6 --proxy http://$host_tcp_proxy:$port_tcp_proxy -v" );
249 like($res->output, qr/^\* proxy_resolves_hostname: 1/m, "proxy is used, there are no preventative measures ");
250 is( $res->return_code, 0, "Using proxy http://$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http_ipv6 works" );
251
252 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 --proxy http://$host_tcp_proxy:$port_tcp_proxy -v" );
253 like($res->output, qr/^\* proxy_resolves_hostname: 1/m, "proxy is used, there are no preventative measures ");
254 is( $res->return_code, 0, "Using proxy http://$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http2 works" );
255
256 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http_subdomain --proxy http://$host_tcp_proxy:$port_tcp_proxy -v" );
257 like($res->output, qr/^\* proxy_resolves_hostname: 1/m, "proxy is used, there are no preventative measures ");
258 is( $res->return_code, 0, "Using proxy http://$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http_subdomain works" );
259
260 $res = NPTest->testCmd( "./$plugin -H $host_tls_http --proxy http://$host_tcp_proxy:$port_tcp_proxy -v" );
261 like($res->output, qr/^\* proxy_resolves_hostname: 1/m, "proxy is used, there are no preventative measures ");
262 is( $res->return_code, 0, "Using proxy http://$host_tcp_proxy:$port_tcp_proxy to connect to $host_tls_http works" );
263
264 # Noproxy '*' should prevent using proxy in any setting, even if its specified
265 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http_subdomain --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy \"\*\" -v" );
266 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used since noproxy has \"\*\" ");
267 is( $res->return_code, 0, "Should reach $host_tcp_http_subdomain with or without proxy." );
268
269 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv4 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy \"\*\" -v" );
270 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used since noproxy has \"\*\" ");
271 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv4 with or without proxy." );
272
273 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv6 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy \"\*\" -v" );
274 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used since noproxy has \"\*\" ");
275 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv6 with or without proxy." );
276
277 # Noproxy domain should prevent using proxy for subdomains of that domain
278 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http_subdomain --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy $host_tcp_http -v" );
279 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used since subdomain: $host_tcp_http_subdomain is under a noproxy domain: $host_tcp_http");
280 is( $res->return_code, 0, "Should reach $host_tcp_http_subdomain with or without proxy." );
281
282 # Noproxy should prevent using IP matches if an IP is found directly
283 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv4 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy $host_tcp_http_ipv4 -v" );
284 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used since IP address: $host_tcp_http_ipv4 is added into noproxy: $host_tcp_http_ipv4");
285 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv4 with or without proxy." );
286
287 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv6 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy $host_tcp_http_ipv6 -v" );
288 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used since IP address: $host_tcp_http_ipv6 is added into noproxy: $host_tcp_http_ipv6");
289 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv6 with or without proxy." );
290
291 # Noproxy should prevent using IP matches if a CIDR region that contains that Ip is used directly.
292 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv4 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy $host_tcp_http_ipv4_cidr_1 -v" );
293 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used since IP address: $host_tcp_http_ipv4 is inside CIDR range: $host_tcp_http_ipv4_cidr_1");
294 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv4 with or without proxy." );
295
296 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv4 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy $host_tcp_http_ipv4_cidr_2 -v" );
297 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used since IP address: $host_tcp_http_ipv4 is inside CIDR range: $host_tcp_http_ipv4_cidr_2");
298 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv4 with or without proxy." );
299
300 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv6 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy $host_tcp_http_ipv6_cidr_1 -v " );
301 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used since IP address: $host_tcp_http_ipv6 is inside CIDR range: $host_tcp_http_ipv6_cidr_1");
302 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv6 with or without proxy." );
303
304 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv6 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy $host_tcp_http_ipv6_cidr_2 -v" );
305 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used since IP address: $host_tcp_http_ipv6 is inside CIDR range: $host_tcp_http_ipv6_cidr_2");
306 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv6 with or without proxy." );
307
308 # Noproxy should discern over different types of proxy schemes
309 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http --proxy http://$host_tcp_proxy:$port_tcp_proxy -v" );
310 like($res->output, qr/^\* proxy_resolves_hostname: 1/m, "proxy is used for resolving hostname, and is using scheme http ");
311 is( $res->return_code, 0, "Using proxy http:$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http works" );
312
313 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http --proxy https://$host_tcp_proxy:$port_tcp_proxy -v" );
314 like($res->output, qr/^\* proxy_resolves_hostname: 1/m, "proxy is used for resolving hostname, and is using scheme https");
315 # Squid is not configured for https
316 # is( $res->return_code, 0, "Using proxy https:$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http works" );
317
318 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http --proxy socks4://$host_tcp_proxy:$port_tcp_proxy -v" );
319 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used for resolving hostname, and is using scheme socks4");
320 # Squid is not configured for socks4
321 # is( $res->return_code, 0, "Using proxy socks4:$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http works" );
322
323 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http --proxy socks4a://$host_tcp_proxy:$port_tcp_proxy -v" );
324 like($res->output, qr/^\* proxy_resolves_hostname: 1/m, "proxy is used for resolving hostname, and is using scheme socks4a");
325 # Squid is not configured for socks4a
326 # is( $res->return_code, 0, "Using proxy socks4a:$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http works" );
327
328 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http --proxy socks5://$host_tcp_proxy:$port_tcp_proxy -v" );
329 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used for resolving hostname, and is using scheme socks5");
330 # Squid is not configured for socks5
331 # is( $res->return_code, 0, "Using proxy socks5:$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http works" );
332
333 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http --proxy socks5h://$host_tcp_proxy:$port_tcp_proxy -v" );
334 like($res->output, qr/^\* proxy_resolves_hostname: 1/m, "proxy is used for resolving hostname, and is using scheme socks5h");
335 # Squid is not configured for socks5h
336 # is( $res->return_code, 0, "Using proxy socks5h:$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http works" );
337}
diff --git a/plugins/t/check_disk.t b/plugins/t/check_disk.t
index 0f62fb2b..72a83ea4 100644
--- a/plugins/t/check_disk.t
+++ b/plugins/t/check_disk.t
@@ -80,14 +80,25 @@ if($free_percent_on_mp1 == $avg_free_percent || $free_percent_on_mp2 == $avg_fre
80 die "One mountpoints has average space free - cannot do rest of test"; 80 die "One mountpoints has average space free - cannot do rest of test";
81} 81}
82 82
83my $free_inodes_on_mp1 = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}[2]->{'perfdata'}->[0]->{'value'}->{'value'}; 83my $used_inodes_on_mp1 = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}[2]->{'perfdata'}->[0]->{'value'}->{'value'};
84my $total_inodes_on_mp1 = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}[2]->{'perfdata'}->[0]->{'max'}->{'value'}; 84my $total_inodes_on_mp1 = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}[2]->{'perfdata'}->[0]->{'max'}->{'value'};
85
86my $free_inodes_on_mp1 = $total_inodes_on_mp1 - $used_inodes_on_mp1;
85my $free_inode_percentage_on_mp1 = $free_inodes_on_mp1 / ($total_inodes_on_mp1 / 100); 87my $free_inode_percentage_on_mp1 = $free_inodes_on_mp1 / ($total_inodes_on_mp1 / 100);
86 88
87my $free_inodes_on_mp2 = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[2]->{'perfdata'}->[0]->{'value'}->{'value'}; 89# print("free inodes on mp1: " . $free_inodes_on_mp1 . "\n");
90# print("total inodes on mp1: " . $total_inodes_on_mp1 . "\n");
91# print("free inode percentage on mp1: " . $free_inode_percentage_on_mp1 . "\n");
92
93my $used_inodes_on_mp2 = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[2]->{'perfdata'}->[0]->{'value'}->{'value'};
88my $total_inodes_on_mp2 = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[2]->{'perfdata'}->[0]->{'max'}->{'value'}; 94my $total_inodes_on_mp2 = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[2]->{'perfdata'}->[0]->{'max'}->{'value'};
95my $free_inodes_on_mp2 = $total_inodes_on_mp2 - $used_inodes_on_mp2;
89my $free_inode_percentage_on_mp2 = $free_inodes_on_mp2 / ($total_inodes_on_mp2 / 100); 96my $free_inode_percentage_on_mp2 = $free_inodes_on_mp2 / ($total_inodes_on_mp2 / 100);
90 97
98# print("free inodes on mp2: " . $free_inodes_on_mp2 . "\n");
99# print("total inodes on mp2: " . $total_inodes_on_mp2 . "\n");
100# print("free inode percentage on mp2: " . $free_inode_percentage_on_mp2 . "\n");
101
91my $avg_inode_free_percentage = ceil(($free_inode_percentage_on_mp1 + $free_inode_percentage_on_mp2)/2); 102my $avg_inode_free_percentage = ceil(($free_inode_percentage_on_mp1 + $free_inode_percentage_on_mp2)/2);
92my ($more_inode_free, $less_inode_free); 103my ($more_inode_free, $less_inode_free);
93if ($free_inode_percentage_on_mp1 > $free_inode_percentage_on_mp2) { 104if ($free_inode_percentage_on_mp1 > $free_inode_percentage_on_mp2) {
diff --git a/plugins/tests/check_curl.t b/plugins/tests/check_curl.t
index 52c5ad1c..e027b6f4 100755
--- a/plugins/tests/check_curl.t
+++ b/plugins/tests/check_curl.t
@@ -20,10 +20,15 @@ use Test::More;
20use NPTest; 20use NPTest;
21use FindBin qw($Bin); 21use FindBin qw($Bin);
22 22
23use URI;
24use URI::QueryParam;
25use HTTP::Daemon;
26use HTTP::Daemon::SSL;
27
23$ENV{'LC_TIME'} = "C"; 28$ENV{'LC_TIME'} = "C";
24 29
25my $common_tests = 75; 30my $common_tests = 111;
26my $ssl_only_tests = 8; 31my $ssl_only_tests = 12;
27# Check that all dependent modules are available 32# Check that all dependent modules are available
28eval "use HTTP::Daemon 6.01;"; 33eval "use HTTP::Daemon 6.01;";
29plan skip_all => 'HTTP::Daemon >= 6.01 required' if $@; 34plan skip_all => 'HTTP::Daemon >= 6.01 required' if $@;
@@ -36,7 +41,7 @@ my $plugin = 'check_http';
36$plugin = 'check_curl' if $0 =~ m/check_curl/mx; 41$plugin = 'check_curl' if $0 =~ m/check_curl/mx;
37 42
38# look for libcurl version to see if some advanced checks are possible (>= 7.49.0) 43# look for libcurl version to see if some advanced checks are possible (>= 7.49.0)
39my $advanced_checks = 12; 44my $advanced_checks = 16;
40my $use_advanced_checks = 0; 45my $use_advanced_checks = 0;
41my $required_version = '7.49.0'; 46my $required_version = '7.49.0';
42my $virtual_host = 'www.somefunnyhost.com'; 47my $virtual_host = 'www.somefunnyhost.com';
@@ -186,6 +191,123 @@ sub run_server {
186 $c->send_response('moved to /redirect2'); 191 $c->send_response('moved to /redirect2');
187 } elsif ($r->url->path eq "/redir_timeout") { 192 } elsif ($r->url->path eq "/redir_timeout") {
188 $c->send_redirect( "/timeout" ); 193 $c->send_redirect( "/timeout" );
194 } elsif ($r->url->path =~ m{^/redirect_with_increment}) {
195 # <scheme>://<username>:<password>@<host>:<port>/<path>;<parameters>?<query>#<fragment>
196 # Find every parameter, query , and fragment keys and increment them
197
198 my $content = "";
199
200 # Use URI to help with query/fragment; parse path params manually.
201 my $original_url = $r->url->as_string;
202 $content .= " original_url: ${original_url}\n";
203 my $uri = URI->new($original_url);
204 $content .= " uri: ${uri}\n";
205
206 my $path = $uri->path // '';
207 my $query = $uri->query // '';
208 my $fragment = $uri->fragment // '';
209
210 $content .= " path: ${path}\n";
211 $content .= " query: ${query}\n";
212 $content .= " fragment: ${fragment}\n";
213
214 # split the URI part and parameters. URI package cannot do this
215 # group 1 is captured: anything without a semicolon: ([^;]*)
216 # group 2 is uncaptured: (?:;(.*))?
217 # (?: ... )? prevents capturing the parameter section
218 # inside group 2, ';' matches the first ever semicolon
219 # group3 is captured: any character string : (.*)
220 # \? matches an actual ? mark, which starts the query parameters
221 my ($before_params, $params) = $uri =~ m{^([^;]*)(?:;(.*))?\?};
222 $before_params //= '';
223 $params //= '';
224 $content .= " before_params: ${before_params}\n";
225 $content .= " params: ${params}\n";
226 my @parameter_pairs;
227 if (defined $params && length $params) {
228 for my $p (split /;/, $params) {
229 my ($key,$value) = split /=/, $p, 2;
230 $value //= '';
231 push @parameter_pairs, [ $key, $value ];
232 $content .= " parameter: ${key} -> ${value}\n";
233 }
234 }
235
236 # query parameters are offered directly from the library
237 my @query_form = $uri->query_form;
238 my @query_parameter_pairs;
239 while (@query_form) {
240 my $key = shift @query_form;
241 my $value = shift @query_form;
242 $value //= ''; # there can be valueless keys
243 push @query_parameter_pairs, [ $key, $value ];
244 $content .= " query: ${key} -> ${value}\n";
245 }
246
247 # helper to increment value
248 my $increment = sub {
249 my ($v) = @_;
250 return $v if !defined $v || $v eq '';
251 # numeric integer
252 if ($v =~ /^-?\d+$/) {
253 return $v + 1;
254 }
255 # otherwise -> increment as if its an ascii character
256 # sed replacement syntax, but the $& holds the matched character
257 if (length($v)) {
258 (my $new_v = $v) =~ s/./chr(ord($&) + 1)/ge;
259 return $new_v;
260 }
261 };
262
263 # increment values in pairs
264 for my $pair (@parameter_pairs) {
265 $pair->[1] = $increment->($pair->[1]);
266 $content .= " parameter new: " . $pair->[0] . " -> " . $pair->[1] . "\n";
267 }
268 for my $pair (@query_parameter_pairs) {
269 $pair->[1] = $increment->($pair->[1]);
270 $content .= " query parameter new: " . $pair->[0] . " -> " . $pair->[1] . "\n";
271 }
272
273 # rebuild strings
274 my $new_parameter_str = join(';', map { $_->[0] . '=' . $_->[1] } @parameter_pairs);
275 $content .= " new_parameter_str: ${new_parameter_str}\n";
276
277 # library can rebuild from an array
278 my @new_query_form;
279 for my $p (@query_parameter_pairs) { push @new_query_form, $p->[0], $p->[1] }
280
281 my $new_fragment_str = '';
282 for my $pair (@parameter_pairs) {
283 my $key = $pair->[0];
284 my $value = $pair->[1];
285 if ($key eq "fragment") {
286 $new_fragment_str = $value
287 }
288 }
289 $content .= " new_fragment_str: ${new_fragment_str}\n";
290
291 # construct new URI using the library
292 my $new_uri = URI->new('');
293 $new_uri->path( $before_params . ($new_parameter_str ? ';' . $new_parameter_str : '') );
294 $new_uri->query_form( \@new_query_form ) if @new_query_form;
295 $new_uri->fragment( $new_fragment_str ) if $new_fragment_str ne '';
296 $content .= " new_uri: ${new_uri}\n";
297
298 # Redirect until fail_count or redirect_count reaches 3
299 if ($new_uri =~ /fail_count=3/){
300 $c->send_error(HTTP::Status->RC_FORBIDDEN, "fail count reached 3, url path:" . $r->url->path );
301 } elsif ($new_uri =~ /redirect_count=3/){
302 $c->send_response(HTTP::Response->new( 200, 'OK', undef , $content ));
303 } elsif ($new_uri =~ /location_redirect_count=3/){
304 $c->send_basic_header(302);
305 $c->send_header("Location", "$new_uri" );
306 $c->send_crlf;
307 $c->send_response("$content \n moved to $new_uri");
308 } else {
309 $c->send_redirect( $new_uri->as_string, 301, $content );
310 }
189 } elsif ($r->url->path eq "/timeout") { 311 } elsif ($r->url->path eq "/timeout") {
190 # Keep $c from being destroyed, but prevent severe leaks 312 # Keep $c from being destroyed, but prevent severe leaks
191 unshift @persist, $c; 313 unshift @persist, $c;
@@ -215,7 +337,7 @@ sub run_server {
215 return($chunk); 337 return($chunk);
216 })); 338 }));
217 } else { 339 } else {
218 $c->send_error(HTTP::Status->RC_FORBIDDEN); 340 $c->send_error(HTTP::Status->RC_FORBIDDEN, "unknown url path:" . $r->url->path );
219 } 341 }
220 $c->close; 342 $c->close;
221 } 343 }
@@ -288,6 +410,41 @@ SKIP: {
288 $result = NPTest->testCmd( $cmd ); 410 $result = NPTest->testCmd( $cmd );
289 is( $result->return_code, 0, $cmd); 411 is( $result->return_code, 0, $cmd);
290 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); 412 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
413
414 # curlopt proxy/noproxy parsing tests, ssl disabled
415 {
416 # Make a scope and change environment variables here, to not mess them up for other tests using environment variables
417
418 local $ENV{"http_proxy"} = 'http://proxy.example.com:8080';
419 $cmd = "$command -u /statuscode/200 -v";
420 $result = NPTest->testCmd( $cmd );
421 like( $result->output, '/.*CURLOPT_PROXY: http://proxy.example.com:8080 */', "Correctly took 'http_proxy' environment variable: ".$result->output );
422 delete($ENV{"http_proxy"});
423
424 local $ENV{"http_proxy"} = 'http://taken.proxy.example:8080';
425 local $ENV{"HTTP_PROXY"} = 'http://discarded.proxy.example:8080';
426 $cmd = "$command -u /statuscode/200 -v";
427 $result = NPTest->testCmd( $cmd );
428 like( $result->output, '/.*CURLOPT_PROXY: http://taken.proxy.example:8080 */', "Correctly took 'http_proxy' environment variable over 'HTTP_PROXY': ".$result->output );
429 delete(local $ENV{"http_proxy"});
430 delete(local $ENV{"HTTP_PROXY"});
431
432 local $ENV{"http_proxy"} = 'http://discarded1.proxy.example:8080';
433 local $ENV{"HTTP_PROXY"} = 'http://discarded2.proxy.example:8080';
434 $cmd = "$command -u /statuscode/200 -x 'http://taken.proxy.example:8080' -v";
435 $result = NPTest->testCmd( $cmd );
436 like( $result->output, '/.*CURLOPT_PROXY: http://taken.proxy.example:8080 */', "Argument -x overwrote 'http_proxy' and 'HTTP_PROXY' environment variables: ".$result->output );
437 delete(local $ENV{"http_proxy"});
438 delete(local $ENV{"HTTP_PROXY"});
439
440 local $ENV{"http_proxy"} = 'http://discarded1.proxy.example:8080';
441 local $ENV{"HTTP_PROXY"} = 'http://discarded2.proxy.example:8080';
442 $cmd = "$command -u /statuscode/200 --proxy 'http://taken.example.com:8080' -v";
443 $result = NPTest->testCmd( $cmd );
444 like( $result->output, '/.*CURLOPT_PROXY: http://taken.example.com:8080 */', "Argument --proxy overwrote 'http_proxy' and 'HTTP_PROXY' environment variables: ".$result->output );
445 delete(local $ENV{"http_proxy"});
446 delete(local $ENV{"HTTP_PROXY"});
447 }
291} 448}
292 449
293# and the same for SSL 450# and the same for SSL
@@ -310,6 +467,41 @@ SKIP: {
310 $result = NPTest->testCmd( $cmd ); 467 $result = NPTest->testCmd( $cmd );
311 is( $result->return_code, 0, $cmd); 468 is( $result->return_code, 0, $cmd);
312 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); 469 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
470
471 # curlopt proxy/noproxy parsing tests, ssl enabled
472 {
473 # Make a scope and change environment variables here, to not mess them up for other tests using environment variables
474
475 local $ENV{"https_proxy"} = 'http://proxy.example.com:8080';
476 $cmd = "$command -u /statuscode/200 --ssl -v";
477 $result = NPTest->testCmd( $cmd );
478 like( $result->output, '/.*CURLOPT_PROXY: http://proxy.example.com:8080 */', "Correctly took 'https_proxy' environment variable: ".$result->output );
479 delete($ENV{"https_proxy"});
480
481 local $ENV{"https_proxy"} = 'http://taken.proxy.example:8080';
482 local $ENV{"HTTPS_PROXY"} = 'http://discarded.proxy.example:8080';
483 $cmd = "$command -u /statuscode/200 --ssl -v";
484 $result = NPTest->testCmd( $cmd );
485 like( $result->output, '/.*CURLOPT_PROXY: http://taken.proxy.example:8080 */', "Correctly took 'https_proxy' environment variable over 'HTTPS_PROXY': ".$result->output );
486 delete(local $ENV{"https_proxy"});
487 delete(local $ENV{"HTTPS_PROXY"});
488
489 local $ENV{"https_proxy"} = 'http://discarded1.proxy.example:8080';
490 local $ENV{"HTTPS_PROXY"} = 'http://discarded2.proxy.example:8080';
491 $cmd = "$command -u /statuscode/200 --ssl -x 'http://taken.example.com:8080' -v";
492 $result = NPTest->testCmd( $cmd );
493 like( $result->output, '/.*CURLOPT_PROXY: http://taken.example.com:8080 */', "Argument -x overwrote environment variables 'https_proxy' and 'HTTPS_PROXY': ".$result->output );
494 delete(local $ENV{"https_proxy"});
495 delete(local $ENV{"HTTPS_PROXY"});
496
497 local $ENV{"https_proxy"} = 'http://discarded1.proxy.example:8080';
498 local $ENV{"HTTPS_PROXY"} = 'http://discarded2.proxy.example:8080';
499 $cmd = "$command -u /statuscode/200 --ssl --proxy 'http://taken.example.com:8080' -v";
500 $result = NPTest->testCmd( $cmd );
501 like( $result->output, '/.*CURLOPT_PROXY: http://taken.example.com:8080 */', "Argument --proxy overwrote environment variables 'https_proxy' and 'HTTPS_PROXY': ".$result->output );
502 delete(local $ENV{"https_proxy"});
503 delete(local $ENV{"HTTPS_PROXY"});
504 }
313} 505}
314 506
315 507
@@ -346,7 +538,7 @@ sub run_common_tests {
346 538
347 $result = NPTest->testCmd( "$command -u /header_broken_check" ); 539 $result = NPTest->testCmd( "$command -u /header_broken_check" );
348 is( $result->return_code, 0, "header_check search for string"); 540 is( $result->return_code, 0, "header_check search for string");
349 like( $result->output, '/.*HTTP/1.1 200 OK - 138 bytes in [\d\.]+ second.*/', "Output correct" ); 541 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct" );
350 542
351 my $cmd; 543 my $cmd;
352 $cmd = "$command -u /slow"; 544 $cmd = "$command -u /slow";
@@ -482,6 +674,64 @@ sub run_common_tests {
482 is( $result->return_code, 0, $cmd); 674 is( $result->return_code, 0, $cmd);
483 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); 675 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
484 676
677 # Redirect with increment tests. These are for checking if the url parameters, query parameters and fragment are parsed.
678 # The server at this point has dynamic redirection. It tries to increment values that it sees in these fields, then redirects.
679 # It also appends some debug log and writes it into HTTP content, pass the -vvv parameter to see them.
680
681 $cmd = "$command -u '/redirect_with_increment/path1/path2/path3/path4' --onredirect=follow -vvv";
682 $result = NPTest->testCmd( "$cmd" );
683 is( $result->return_code, 1, $cmd);
684 like( $result->output, '/.*HTTP/1.1 403 Forbidden - \d+ bytes in [\d\.]+ second.*/', "Output correct, redirect_count was not present, got redirected to / : ".$result->output );
685
686 # redirect_count=0 is parsed as a parameter and incremented. When it goes up to 3, the redirection returns HTTP OK
687 $cmd = "$command -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv";
688 $result = NPTest->testCmd( "$cmd" );
689 is( $result->return_code, 0, $cmd);
690 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, redirect_count went up to 3, and returned OK: ".$result->output );
691
692 # location_redirect_count=0 goes up to 3, which uses the HTTP 302 style of redirection with 'Location' header
693 $cmd = "$command -u '/redirect_with_increment/path1/path2;location_redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv";
694 $result = NPTest->testCmd( "$cmd" );
695 is( $result->return_code, 0, $cmd);
696 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output );
697
698 # fail_count parameter may also go up to 3, which returns a HTTP 403
699 $cmd = "$command -u '/redirect_with_increment/path1/path2;redirect_count=0;fail_count=2' --onredirect=follow -vvv";
700 $result = NPTest->testCmd( "$cmd" );
701 is( $result->return_code, 1, $cmd);
702 like( $result->output, '/.*HTTP/1.1 403 Forbidden - \d+ bytes in [\d\.]+ second.*/', "Output correct, early due to fail_count reaching 3: ".$result->output );
703
704 # redirect_count=0, p1=1 , p2=ab => redirect_count=1, p1=2 , p2=bc => redirect_count=2, p1=3 , p2=cd => redirect_count=3 , p1=4 , p2=de
705 # Last visited URI returns HTTP OK instead of redirect, and the one before that contains the new_uri in its content
706 $cmd = "$command -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv";
707 $result = NPTest->testCmd( "$cmd" );
708 is( $result->return_code, 0, $cmd);
709 like( $result->output, '/.*redirect_count=3;p1=4;p2=de\?*/', "Output correct, parsed and incremented both parameters p1 and p2 : ".$result->output );
710 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output );
711
712 # Same incrementation as before, uses the query parameters that come after the first '?' : qp1 and qp2
713 $cmd = "$command -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv";
714 $result = NPTest->testCmd( "$cmd" );
715 is( $result->return_code, 0, $cmd);
716 like( $result->output, '/.*\?qp1=13&qp2=no*/', "Output correct, parsed and incremented both query parameters qp1 and qp2 : ".$result->output );
717 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output );
718
719 # Check if the query parameter order is kept intact
720 $cmd = "$command -u '/redirect_with_increment;redirect_count=0;?qp0=0&qp1=1&qp2=2&qp3=3&qp4=4&qp5=5' --onredirect=follow -vvv";
721 $result = NPTest->testCmd( "$cmd" );
722 is( $result->return_code, 0, $cmd);
723 like( $result->output, '/.*\?qp0=3&qp1=4&qp2=5&qp3=6&qp4=7&qp5=8*/', "Output correct, parsed and incremented query parameters qp1,qp2,qp3,qp4,qp5 in order : ".$result->output );
724 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output );
725
726 # The fragment is passed as another parameter.
727 # During the server redirects the fragment will be set to its value, if such a key is present.
728 # 'ebiil' => 'fcjjm' => 'gdkkn' => 'hello'
729 $cmd = "$command -u '/redirect_with_increment/path1/path2;redirect_count=0;fragment=ebiil?qp1=0' --onredirect=follow -vvv";
730 $result = NPTest->testCmd( "$cmd" );
731 is( $result->return_code, 0, $cmd);
732 like( $result->output, '/.*redirect_count=3;fragment=hello\?qp1=3#hello*/', "Output correct, fragments are specified by server and followed by check_curl: ".$result->output );
733 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output );
734
485 # These tests may block 735 # These tests may block
486 # stickyport - on full urlS port is set back to 80 otherwise 736 # stickyport - on full urlS port is set back to 80 otherwise
487 $cmd = "$command -f stickyport -u /redir_external -t 5 -s redirected"; 737 $cmd = "$command -f stickyport -u /redir_external -t 5 -s redirected";
@@ -532,4 +782,63 @@ sub run_common_tests {
532 $result = NPTest->testCmd( $cmd, 5 ); 782 $result = NPTest->testCmd( $cmd, 5 );
533 }; 783 };
534 is( $@, "", $cmd ); 784 is( $@, "", $cmd );
785
786 # curlopt proxy/noproxy parsing tests
787 {
788 # Make a scope and change environment variables here, to not mess them up for other tests using environment variables
789
790 local $ENV{"no_proxy"} = 'internal.acme.org';
791 $cmd = "$command -u /statuscode/200 -v";
792 $result = NPTest->testCmd( $cmd );
793 like( $result->output, '/.* curl CURLOPT_NOPROXY: internal.acme.org */', "Correctly took 'no_proxy' environment variable: ".$result->output );
794 delete($ENV{"no_proxy"});
795
796 local $ENV{"no_proxy"} = 'taken.acme.org';
797 local $ENV{"NO_PROXY"} = 'discarded.acme.org';
798 $cmd = "$command -u /statuscode/200 -v";
799 $result = NPTest->testCmd( $cmd );
800 is( $result->return_code, 0, $cmd);
801 like( $result->output, '/.*CURLOPT_NOPROXY: taken.acme.org*/', "Correctly took 'no_proxy' environment variable over 'NO_PROXY': ".$result->output );
802 delete(local $ENV{"no_proxy"});
803 delete(local $ENV{"NO_PROXY"});
804
805 local $ENV{"no_proxy"} = 'taken.acme.org';
806 local $ENV{"NO_PROXY"} = 'discarded.acme.org';
807 $cmd = "$command -u /statuscode/200 --noproxy 'taken.acme.org' -v";
808 $result = NPTest->testCmd( $cmd );
809 is( $result->return_code, 0, $cmd);
810 like( $result->output, '/.*CURLOPT_NOPROXY: taken.acme.org*/', "Argument --noproxy overwrote environment variables 'no_proxy' and 'NO_PROXY': ".$result->output );
811 delete(local $ENV{"no_proxy"});
812 delete(local $ENV{"NO_PROXY"});
813
814 $cmd = "$command -u /statuscode/200 --noproxy 'internal1.acme.org,internal2.acme.org,internal3.acme.org' -v";
815 $result = NPTest->testCmd( $cmd );
816 is( $result->return_code, 0, $cmd);
817 like( $result->output, '/.*CURLOPT_NOPROXY: internal1.acme.org,internal2.acme.org,internal3.acme.org*/', "Argument --noproxy read multiple noproxy domains: ".$result->output );
818
819 $cmd = "$command -u /statuscode/200 --noproxy '10.11.12.13,256.256.256.256,0.0.0.0,192.156.0.0/22,10.0.0.0/4' -v";
820 $result = NPTest->testCmd( $cmd );
821 is( $result->return_code, 0, $cmd);
822 like( $result->output, '/.*CURLOPT_NOPROXY: 10.11.12.13,256.256.256.256,0.0.0.0,192.156.0.0/22,10.0.0.0/4*/', "Argument --noproxy took multiple noproxy domains: ".$result->output );
823
824 $cmd = "$command -u /statuscode/200 --noproxy '0123:4567:89AB:CDEF:0123:4567:89AB:CDEF,0123::CDEF,0123:4567/96,[::1],::1,[1234::5678:ABCD/4]' -v";
825 $result = NPTest->testCmd( $cmd );
826 is( $result->return_code, 0, $cmd);
827 like( $result->output, '/.*CURLOPT_NOPROXY: 0123:4567:89AB:CDEF:0123:4567:89AB:CDEF,0123::CDEF,0123:4567\/96,\[::1\],::1,\[1234::5678:ABCD\/4\].*/', "Argument --noproxy took multiple noproxy domains: ".$result->output );
828
829 $cmd = "$command -u /statuscode/200 --noproxy '300.400.500.600,1.2.3,XYZD:0123::,1:2:3:4:5:6:7,1::2::3,1.1.1.1/64,::/256' -v";
830 $result = NPTest->testCmd( $cmd );
831 is( $result->return_code, 0, $cmd);
832
833 $cmd = "$command -u /statuscode/200 --proxy http://proxy.example.com:8080 --noproxy '*' -v";
834 $result = NPTest->testCmd( $cmd );
835 is( $result->return_code, 0, $cmd);
836 like( $result->output, '/.*proxy_resolves_hostname: 0.*/', "Proxy will not be used due to '*' in noproxy: ".$result->output );
837
838 $cmd = "$command -u /statuscode/200 --proxy http://proxy.example.com:8080 --noproxy '127.0.0.1' -v";
839 $result = NPTest->testCmd( $cmd );
840 is( $result->return_code, 0, $cmd);
841 like( $result->output, '/.*proxy_resolves_hostname: 0.*/', "Proxy will not be used due to '127.0.0.1' in noproxy: ".$result->output );
842 }
843
535} 844}
diff --git a/plugins/tests/check_nt.t b/plugins/tests/check_nt.t
deleted file mode 100755
index 223d4933..00000000
--- a/plugins/tests/check_nt.t
+++ /dev/null
@@ -1,80 +0,0 @@
1#! /usr/bin/perl -w -I ..
2#
3# Test check_nt by having a stub check_nt daemon
4#
5
6use strict;
7use Test::More;
8use NPTest;
9use FindBin qw($Bin);
10
11use IO::Socket;
12use IO::Select;
13use POSIX;
14
15my $port = 50000 + int(rand(1000));
16
17my $pid = fork();
18if ($pid) {
19 # Parent
20 #print "parent\n";
21 # give our webserver some time to startup
22 sleep(1);
23} else {
24 # Child
25 #print "child\n";
26
27 my $server = IO::Socket::INET->new(
28 LocalPort => $port,
29 Type => SOCK_STREAM,
30 Reuse => 1,
31 Proto => "tcp",
32 Listen => 10,
33 ) or die "Cannot be a tcp server on port $port: $@";
34
35 $server->autoflush(1);
36
37 print "Please contact me at port $port\n";
38 while (my $client = $server->accept ) {
39 my $data = "";
40 my $rv = $client->recv($data, POSIX::BUFSIZ, 0);
41
42 my ($password, $command, $arg) = split('&', $data);
43
44 if ($command eq "4") {
45 if ($arg eq "c") {
46 print $client "930000000&1000000000";
47 } elsif ($arg eq "d") {
48 print $client "UNKNOWN: Drive is not a fixed drive";
49 }
50 }
51 }
52 exit;
53}
54
55END { if ($pid) { print "Killing $pid\n"; kill "INT", $pid } };
56
57if ($ARGV[0] && $ARGV[0] eq "-d") {
58 sleep 1000;
59}
60
61if (-x "./check_nt") {
62 plan tests => 5;
63} else {
64 plan skip_all => "No check_nt compiled";
65}
66
67my $result;
68my $command = "./check_nt -H 127.0.0.1 -p $port";
69
70$result = NPTest->testCmd( "$command -v USEDDISKSPACE -l c" );
71is( $result->return_code, 0, "USEDDISKSPACE c");
72is( $result->output, q{c:\ - total: 0.93 Gb - used: 0.07 Gb (7%) - free 0.87 Gb (93%) | 'c:\ Used Space'=0.07Gb;0.00;0.00;0.00;0.93}, "Output right" );
73
74$result = NPTest->testCmd( "$command -v USEDDISKSPACE -l d" );
75is( $result->return_code, 3, "USEDDISKSPACE d - invalid");
76is( $result->output, "Free disk space : Invalid drive", "Output right" );
77
78$result = NPTest->testCmd( "./check_nt -v USEDDISKSPACE -l d" );
79is( $result->return_code, 3, "Fail if -H missing");
80
diff --git a/plugins/utils.c b/plugins/utils.c
index 41fe5fcf..dc6f5a85 100644
--- a/plugins/utils.c
+++ b/plugins/utils.c
@@ -40,7 +40,6 @@ extern const char *progname;
40#define STRLEN 64 40#define STRLEN 64
41#define TXTBLK 128 41#define TXTBLK 128
42 42
43time_t start_time, end_time;
44 43
45void usage(const char *msg) { 44void usage(const char *msg) {
46 printf("%s\n", msg); 45 printf("%s\n", msg);
diff --git a/plugins/utils.h b/plugins/utils.h
index 1f0e021b..68ff1630 100644
--- a/plugins/utils.h
+++ b/plugins/utils.h
@@ -32,8 +32,6 @@ suite of plugins. */
32void support(void); 32void support(void);
33void print_revision(const char *, const char *); 33void print_revision(const char *, const char *);
34 34
35extern time_t start_time, end_time;
36
37/* Test input types */ 35/* Test input types */
38 36
39bool is_integer(char *); 37bool is_integer(char *);