From c7274d56274fa1a38837510b1cbde447e73e9b63 Mon Sep 17 00:00:00 2001
From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com>
Date: Wed, 10 Sep 2025 13:40:21 +0200
Subject: netutils.h: clang-format
---
plugins/netutils.h | 131 ++++++++++++++++++++++++++---------------------------
1 file changed, 65 insertions(+), 66 deletions(-)
(limited to 'plugins/netutils.h')
diff --git a/plugins/netutils.h b/plugins/netutils.h
index a95057e0..0ca524f2 100644
--- a/plugins/netutils.h
+++ b/plugins/netutils.h
@@ -1,63 +1,64 @@
/*****************************************************************************
-*
-* Monitoring Plugins net utilities include file
-*
-* License: GPL
-* Copyright (c) 1999 Ethan Galstad (nagios@nagios.org)
-* Copyright (c) 2003-2007 Monitoring Plugins Development Team
-*
-* Description:
-*
-* This file contains common include files and function definitions
-* used in many of the plugins.
-*
-*
-* This program is free software: you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation, either version 3 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with this program. If not, see .
-*
-*
-*****************************************************************************/
+ *
+ * Monitoring Plugins net utilities include file
+ *
+ * License: GPL
+ * Copyright (c) 1999 Ethan Galstad (nagios@nagios.org)
+ * Copyright (c) 2003-2007 Monitoring Plugins Development Team
+ *
+ * Description:
+ *
+ * This file contains common include files and function definitions
+ * used in many of the plugins.
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ *
+ *****************************************************************************/
#ifndef _NETUTILS_H_
#define _NETUTILS_H_
#include "common.h"
+#include "states.h"
#include "utils.h"
#include
#include
#include
#ifdef HAVE_SYS_UN_H
-# include
-# ifndef UNIX_PATH_MAX
- /* linux uses this, on sun it's hard-coded at 108 without a define, on BSD at 104 */
-# define UNIX_PATH_MAX 104
-# endif /* UNIX_PATH_MAX */
-#endif /* HAVE_SYS_UN_H */
+# include
+# ifndef UNIX_PATH_MAX
+/* linux uses this, on sun it's hard-coded at 108 without a define, on BSD at 104 */
+# define UNIX_PATH_MAX 104
+# endif /* UNIX_PATH_MAX */
+#endif /* HAVE_SYS_UN_H */
#ifndef HOST_MAX_BYTES
-# define HOST_MAX_BYTES 255
+# define HOST_MAX_BYTES 255
#endif
/* process_request and wrapper macros */
-#define process_tcp_request(addr, port, sbuf, rbuf, rsize) \
+#define process_tcp_request(addr, port, sbuf, rbuf, rsize) \
process_request(addr, port, IPPROTO_TCP, sbuf, rbuf, rsize)
-#define process_udp_request(addr, port, sbuf, rbuf, rsize) \
+#define process_udp_request(addr, port, sbuf, rbuf, rsize) \
process_request(addr, port, IPPROTO_UDP, sbuf, rbuf, rsize)
-int process_tcp_request2 (const char *address, int port,
- const char *sbuffer, char *rbuffer, int rsize);
-int process_request (const char *address, int port, int proto,
- const char *sbuffer, char *rbuffer, int rsize);
+int process_tcp_request2(const char *address, int port, const char *sbuffer, char *rbuffer,
+ int rsize);
+int process_request(const char *address, int port, int proto, const char *sbuffer, char *rbuffer,
+ int rsize);
/* my_connect and wrapper macros */
#define my_tcp_connect(addr, port, s) np_net_connect(addr, port, s, IPPROTO_TCP)
@@ -65,25 +66,22 @@ int process_request (const char *address, int port, int proto,
int np_net_connect(const char *address, int port, int *sd, int proto);
/* send_request and wrapper macros */
-#define send_tcp_request(s, sbuf, rbuf, rsize) \
- send_request(s, IPPROTO_TCP, sbuf, rbuf, rsize)
-#define send_udp_request(s, sbuf, rbuf, rsize) \
- send_request(s, IPPROTO_UDP, sbuf, rbuf, rsize)
-int send_request (int sd, int proto, const char *send_buffer, char *recv_buffer, int recv_size);
-
+#define send_tcp_request(s, sbuf, rbuf, rsize) send_request(s, IPPROTO_TCP, sbuf, rbuf, rsize)
+#define send_udp_request(s, sbuf, rbuf, rsize) send_request(s, IPPROTO_UDP, sbuf, rbuf, rsize)
+int send_request(int sd, int proto, const char *send_buffer, char *recv_buffer, int recv_size);
/* "is_*" wrapper macros and functions */
-bool is_host (const char *);
-bool is_addr (const char *);
-int dns_lookup (const char *, struct sockaddr_storage *, int);
+bool is_host(const char *);
+bool is_addr(const char *);
+int dns_lookup(const char *, struct sockaddr_storage *, int);
void host_or_die(const char *str);
#define resolve_host_or_addr(addr, family) dns_lookup(addr, NULL, family)
-#define is_inet_addr(addr) resolve_host_or_addr(addr, AF_INET)
+#define is_inet_addr(addr) resolve_host_or_addr(addr, AF_INET)
#ifdef USE_IPV6
-# define is_inet6_addr(addr) resolve_host_or_addr(addr, AF_INET6)
-# define is_hostname(addr) resolve_host_or_addr(addr, address_family)
+# define is_inet6_addr(addr) resolve_host_or_addr(addr, AF_INET6)
+# define is_hostname(addr) resolve_host_or_addr(addr, address_family)
#else
-# define is_hostname(addr) resolve_host_or_addr(addr, AF_INET)
+# define is_hostname(addr) resolve_host_or_addr(addr, AF_INET)
#endif
extern unsigned int socket_timeout;
@@ -92,25 +90,26 @@ extern int econn_refuse_state;
extern bool was_refused;
extern int address_family;
-void socket_timeout_alarm_handler (int) __attribute__((noreturn));
+void socket_timeout_alarm_handler(int) __attribute__((noreturn));
/* SSL-Related functionality */
#ifdef HAVE_SSL
-# define MP_SSLv2 1
-# define MP_SSLv3 2
-# define MP_TLSv1 3
-# define MP_TLSv1_1 4
-# define MP_TLSv1_2 5
-# define MP_SSLv2_OR_NEWER 6
-# define MP_SSLv3_OR_NEWER 7
-# define MP_TLSv1_OR_NEWER 8
-# define MP_TLSv1_1_OR_NEWER 9
-# define MP_TLSv1_2_OR_NEWER 10
+# define MP_SSLv2 1
+# define MP_SSLv3 2
+# define MP_TLSv1 3
+# define MP_TLSv1_1 4
+# define MP_TLSv1_2 5
+# define MP_SSLv2_OR_NEWER 6
+# define MP_SSLv3_OR_NEWER 7
+# define MP_TLSv1_OR_NEWER 8
+# define MP_TLSv1_1_OR_NEWER 9
+# define MP_TLSv1_2_OR_NEWER 10
/* maybe this could be merged with the above np_net_connect, via some flags */
int np_net_ssl_init(int sd);
int np_net_ssl_init_with_hostname(int sd, char *host_name);
int np_net_ssl_init_with_hostname_and_version(int sd, char *host_name, int version);
-int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int version, char *cert, char *privkey);
+int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int version, char *cert,
+ char *privkey);
void np_net_ssl_cleanup();
int np_net_ssl_write(const void *buf, int num);
int np_net_ssl_read(void *buf, int num);
--
cgit v1.2.3-74-g34f1
From 572ad994b136c443c5d59509a28b8343c3e40ab3 Mon Sep 17 00:00:00 2001
From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com>
Date: Wed, 10 Sep 2025 13:41:22 +0200
Subject: plugins-netutils: return proper state from test functions
---
plugins/netutils.h | 2 +-
plugins/sslutils.c | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)
(limited to 'plugins/netutils.h')
diff --git a/plugins/netutils.h b/plugins/netutils.h
index 0ca524f2..c53b3cef 100644
--- a/plugins/netutils.h
+++ b/plugins/netutils.h
@@ -113,7 +113,7 @@ int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int
void np_net_ssl_cleanup();
int np_net_ssl_write(const void *buf, int num);
int np_net_ssl_read(void *buf, int num);
-int np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit);
+mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit);
#endif /* HAVE_SSL */
#endif /* _NETUTILS_H_ */
diff --git a/plugins/sslutils.c b/plugins/sslutils.c
index 92e0cc84..b20a2b2c 100644
--- a/plugins/sslutils.c
+++ b/plugins/sslutils.c
@@ -30,6 +30,7 @@
#include "common.h"
#include "netutils.h"
#include "../lib/monitoringplug.h"
+#include "states.h"
#ifdef HAVE_SSL
static SSL_CTX *ctx = NULL;
@@ -318,7 +319,7 @@ int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn,
# endif /* USE_OPENSSL */
}
-int np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit) {
+mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit) {
# ifdef USE_OPENSSL
X509 *certificate = NULL;
certificate = SSL_get_peer_certificate(s);
--
cgit v1.2.3-74-g34f1
From f5f4a021a2760d553e3e4cdedd291eb815750369 Mon Sep 17 00:00:00 2001
From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com>
Date: Mon, 15 Sep 2025 01:57:40 +0200
Subject: Add new cert check function
---
plugins/check_curl.c | 3 +-
plugins/netutils.h | 3 +-
plugins/sslutils.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 139 insertions(+), 2 deletions(-)
(limited to 'plugins/netutils.h')
diff --git a/plugins/check_curl.c b/plugins/check_curl.c
index 680ecef7..b1021045 100644
--- a/plugins/check_curl.c
+++ b/plugins/check_curl.c
@@ -569,8 +569,9 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state
sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK);
}
} else if (errcode == REG_NOMATCH) {
- xasprintf(&sc_body_regex.output, "%s not", sc_body_regex.output);
// got no match
+ xasprintf(&sc_body_regex.output, "%s not", sc_body_regex.output);
+
if (config.invert_regex) {
sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK);
} else {
diff --git a/plugins/netutils.h b/plugins/netutils.h
index c53b3cef..6adb8e01 100644
--- a/plugins/netutils.h
+++ b/plugins/netutils.h
@@ -32,6 +32,7 @@
#define _NETUTILS_H_
#include "common.h"
+#include "output.h"
#include "states.h"
#include "utils.h"
#include
@@ -114,6 +115,6 @@ void np_net_ssl_cleanup();
int np_net_ssl_write(const void *buf, int num);
int np_net_ssl_read(void *buf, int num);
mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit);
+mp_subcheck mp_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit);
#endif /* HAVE_SSL */
-
#endif /* _NETUTILS_H_ */
diff --git a/plugins/sslutils.c b/plugins/sslutils.c
index bea1307f..3ce6afed 100644
--- a/plugins/sslutils.c
+++ b/plugins/sslutils.c
@@ -26,6 +26,7 @@
*
*****************************************************************************/
+#include "output.h"
#define MAX_CN_LENGTH 256
#include "common.h"
#include "netutils.h"
@@ -322,4 +323,138 @@ mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_cr
# endif /* USE_OPENSSL */
}
+mp_subcheck mp_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn,
+ int days_till_exp_crit) {
+ mp_subcheck sc_cert = mp_subcheck_init();
+# ifdef USE_OPENSSL
+ if (!certificate) {
+ xasprintf(&sc_cert.output, _("No server certificate present to inspect"));
+ sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
+ return sc_cert;
+ }
+
+ /* Extract CN from certificate subject */
+ X509_NAME *subj = X509_get_subject_name(certificate);
+
+ if (!subj) {
+ xasprintf(&sc_cert.output, _("Cannot retrieve certificate subject"));
+ sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
+ return sc_cert;
+ }
+
+ char commonName[MAX_CN_LENGTH] = "";
+ int cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, commonName, sizeof(commonName));
+ if (cnlen == -1) {
+ strcpy(commonName, _("Unknown CN"));
+ }
+
+ /* Retrieve timestamp of certificate */
+ ASN1_STRING *expiry_timestamp = X509_get_notAfter(certificate);
+
+ int offset = 0;
+ struct tm stamp = {};
+ /* Generate tm structure to process timestamp */
+ if (expiry_timestamp->type == V_ASN1_UTCTIME) {
+ if (expiry_timestamp->length < 10) {
+ xasprintf(&sc_cert.output, _("Wrong time format in certificate"));
+ sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
+ return sc_cert;
+ }
+
+ stamp.tm_year = (expiry_timestamp->data[0] - '0') * 10 + (expiry_timestamp->data[1] - '0');
+ if (stamp.tm_year < 50) {
+ stamp.tm_year += 100;
+ }
+
+ offset = 0;
+ } else {
+ if (expiry_timestamp->length < 12) {
+ xasprintf(&sc_cert.output, _("Wrong time format in certificate"));
+ sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
+ return sc_cert;
+ }
+ stamp.tm_year = (expiry_timestamp->data[0] - '0') * 1000 +
+ (expiry_timestamp->data[1] - '0') * 100 +
+ (expiry_timestamp->data[2] - '0') * 10 + (expiry_timestamp->data[3] - '0');
+ stamp.tm_year -= 1900;
+ offset = 2;
+ }
+
+ stamp.tm_mon = (expiry_timestamp->data[2 + offset] - '0') * 10 +
+ (expiry_timestamp->data[3 + offset] - '0') - 1;
+ stamp.tm_mday = (expiry_timestamp->data[4 + offset] - '0') * 10 +
+ (expiry_timestamp->data[5 + offset] - '0');
+ stamp.tm_hour = (expiry_timestamp->data[6 + offset] - '0') * 10 +
+ (expiry_timestamp->data[7 + offset] - '0');
+ stamp.tm_min = (expiry_timestamp->data[8 + offset] - '0') * 10 +
+ (expiry_timestamp->data[9 + offset] - '0');
+ stamp.tm_sec = (expiry_timestamp->data[10 + offset] - '0') * 10 +
+ (expiry_timestamp->data[11 + offset] - '0');
+ stamp.tm_isdst = -1;
+
+ time_t tm_t = timegm(&stamp);
+ double time_left = difftime(tm_t, time(NULL));
+ int days_left = (int)(time_left / 86400);
+ char *timeZone = getenv("TZ");
+ setenv("TZ", "GMT", 1);
+ tzset();
+
+ char timestamp[50] = "";
+ strftime(timestamp, 50, "%c %z", localtime(&tm_t));
+ if (timeZone) {
+ setenv("TZ", timeZone, 1);
+ } else {
+ unsetenv("TZ");
+ }
+
+ tzset();
+
+ int time_remaining;
+ if (days_left > 0 && days_left <= days_till_exp_warn) {
+ xasprintf(&sc_cert.output, _("Certificate '%s' expires in %d day(s) (%s)"), commonName,
+ days_left, timestamp);
+ if (days_left > days_till_exp_crit) {
+ sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING);
+ } else {
+ sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
+ }
+ } else if (days_left == 0 && time_left > 0) {
+ if (time_left >= 3600) {
+ time_remaining = (int)time_left / 3600;
+ } else {
+ time_remaining = (int)time_left / 60;
+ }
+
+ xasprintf(&sc_cert.output, _("Certificate '%s' expires in %u %s (%s)"), commonName,
+ time_remaining, time_left >= 3600 ? "hours" : "minutes", timestamp);
+
+ if (days_left > days_till_exp_crit) {
+ sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING);
+ } else {
+ sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
+ }
+ } else if (time_left < 0) {
+ xasprintf(&sc_cert.output, _("Certificate '%s' expired on %s"), commonName, timestamp);
+ sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
+ } else if (days_left == 0) {
+ xasprintf(&sc_cert.output, _("Certificate '%s' just expired (%s)"), commonName,
+ timestamp);
+ if (days_left > days_till_exp_crit) {
+ sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING);
+ } else {
+ sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
+ }
+ } else {
+ xasprintf(&sc_cert.output, _("Certificate '%s' will expire on %s"), commonName,
+ timestamp);
+ sc_cert = mp_set_subcheck_state(sc_cert, STATE_OK);
+ }
+ X509_free(certificate);
+ return sc_cert;
+# else /* ifndef USE_OPENSSL */
+ xasprintf(&sc_cert.output, _("Plugin does not support checking certificates"));
+ sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING);
+ return sc_cert;
+# endif /* USE_OPENSSL */
+}
#endif /* HAVE_SSL */
--
cgit v1.2.3-74-g34f1
From 811da10fda11b1d0453453040647270700862d8f Mon Sep 17 00:00:00 2001
From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com>
Date: Mon, 15 Sep 2025 15:49:26 +0200
Subject: More refactoring
---
plugins-root/check_icmp.d/check_icmp_helpers.c | 4 +-
plugins/check_load.c | 4 +-
plugins/netutils.c | 224 +++++++++++++------------
plugins/netutils.h | 32 ++--
4 files changed, 139 insertions(+), 125 deletions(-)
(limited to 'plugins/netutils.h')
diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.c b/plugins-root/check_icmp.d/check_icmp_helpers.c
index d56fbd8b..1b96a392 100644
--- a/plugins-root/check_icmp.d/check_icmp_helpers.c
+++ b/plugins-root/check_icmp.d/check_icmp_helpers.c
@@ -76,7 +76,7 @@ check_icmp_state check_icmp_state_init() {
ping_target_create_wrapper ping_target_create(struct sockaddr_storage address) {
ping_target_create_wrapper result = {
- .errorcode = OK,
+ .errorcode = 0,
};
struct sockaddr_storage *tmp_addr = &address;
@@ -88,7 +88,7 @@ ping_target_create_wrapper ping_target_create(struct sockaddr_storage address) {
((struct sockaddr_in *)tmp_addr)->sin_addr.s_addr == INADDR_ANY))) ||
(tmp_addr->ss_family == AF_INET6 &&
(((struct sockaddr_in6 *)tmp_addr)->sin6_addr.s6_addr == in6addr_any.s6_addr))) {
- result.errorcode = ERROR;
+ result.errorcode = 1;
return result;
}
diff --git a/plugins/check_load.c b/plugins/check_load.c
index f7a6f7fd..644cd604 100644
--- a/plugins/check_load.c
+++ b/plugins/check_load.c
@@ -452,8 +452,8 @@ static top_processes_result print_top_consuming_processes(unsigned long n_procs_
top_processes_result result = {
.errorcode = OK,
};
- struct output chld_out;
- struct output chld_err;
+ output chld_out;
+ output chld_err;
if (np_runcmd(PS_COMMAND, &chld_out, &chld_err, 0) != 0) {
fprintf(stderr, _("'%s' exited with non-zero status.\n"), PS_COMMAND);
result.errorcode = ERROR;
diff --git a/plugins/netutils.c b/plugins/netutils.c
index 92c53e4c..b4c6ff0a 100644
--- a/plugins/netutils.c
+++ b/plugins/netutils.c
@@ -30,13 +30,14 @@
#include "common.h"
#include "output.h"
#include "states.h"
+#include
#include "netutils.h"
unsigned int socket_timeout = DEFAULT_SOCKET_TIMEOUT;
-unsigned int socket_timeout_state = STATE_CRITICAL;
-
-int econn_refuse_state = STATE_CRITICAL;
+mp_state_enum socket_timeout_state = STATE_CRITICAL;
+mp_state_enum econn_refuse_state = STATE_CRITICAL;
bool was_refused = false;
+
#if USE_IPV6
int address_family = AF_UNSPEC;
#else
@@ -63,39 +64,40 @@ void socket_timeout_alarm_handler(int sig) {
/* connects to a host on a specified tcp port, sends a string, and gets a
response. loops on select-recv until timeout or eof to get all of a
multi-packet answer */
-int process_tcp_request2(const char *server_address, int server_port, const char *send_buffer,
- char *recv_buffer, int recv_size) {
+mp_state_enum process_tcp_request2(const char *server_address, const int server_port,
+ const char *send_buffer, char *recv_buffer,
+ const int recv_size) {
- int result;
- int send_result;
- int recv_result;
- int sd;
- struct timeval tv;
- fd_set readfds;
- int recv_length = 0;
+ int socket;
- result = np_net_connect(server_address, server_port, &sd, IPPROTO_TCP);
- if (result != STATE_OK) {
+ mp_state_enum connect_result =
+ np_net_connect(server_address, server_port, &socket, IPPROTO_TCP);
+ if (connect_result != STATE_OK) {
return STATE_CRITICAL;
}
- send_result = send(sd, send_buffer, strlen(send_buffer), 0);
+ mp_state_enum result;
+ ssize_t send_result = send(socket, send_buffer, strlen(send_buffer), 0);
if (send_result < 0 || (size_t)send_result != strlen(send_buffer)) {
// printf("%s\n", _("Send failed"));
result = STATE_WARNING;
}
- while (1) {
+ fd_set readfds;
+ ssize_t recv_length = 0;
+ while (true) {
/* wait up to the number of seconds for socket timeout
minus one for data from the host */
- tv.tv_sec = socket_timeout - 1;
- tv.tv_usec = 0;
+ struct timeval timeout = {
+ .tv_sec = socket_timeout - 1,
+ .tv_usec = 0,
+ };
FD_ZERO(&readfds);
- FD_SET(sd, &readfds);
- select(sd + 1, &readfds, NULL, NULL, &tv);
+ FD_SET(socket, &readfds);
+ select(socket + 1, &readfds, NULL, NULL, &timeout);
/* make sure some data has arrived */
- if (!FD_ISSET(sd, &readfds)) { /* it hasn't */
+ if (!FD_ISSET(socket, &readfds)) { /* it hasn't */
if (!recv_length) {
strcpy(recv_buffer, "");
// printf("%s\n", _("No data was received from host!"));
@@ -104,72 +106,69 @@ int process_tcp_request2(const char *server_address, int server_port, const char
recv_buffer[recv_length] = 0;
}
break;
- } else { /* it has */
- recv_result =
- recv(sd, recv_buffer + recv_length, (size_t)recv_size - recv_length - 1, 0);
- if (recv_result == -1) {
- /* recv failed, bail out */
- strcpy(recv_buffer + recv_length, "");
- result = STATE_WARNING;
- break;
- } else if (recv_result == 0) {
- /* end of file ? */
- recv_buffer[recv_length] = 0;
- break;
- } else { /* we got data! */
- recv_length += recv_result;
- if (recv_length >= recv_size - 1) {
- /* buffer full, we're done */
- recv_buffer[recv_size - 1] = 0;
- break;
- }
- }
+ } /* it has */
+
+ ssize_t recv_result =
+ recv(socket, recv_buffer + recv_length, (size_t)(recv_size - recv_length - 1), 0);
+ if (recv_result == -1) {
+ /* recv failed, bail out */
+ strcpy(recv_buffer + recv_length, "");
+ result = STATE_WARNING;
+ break;
+ }
+
+ if (recv_result == 0) {
+ /* end of file ? */
+ recv_buffer[recv_length] = 0;
+ break;
+ }
+
+ /* we got data! */
+ recv_length += recv_result;
+ if (recv_length >= recv_size - 1) {
+ /* buffer full, we're done */
+ recv_buffer[recv_size - 1] = 0;
+ break;
}
/* end if(!FD_ISSET(sd,&readfds)) */
}
- /* end while(1) */
- close(sd);
+ close(socket);
return result;
}
/* connects to a host on a specified port, sends a string, and gets a
response */
-int process_request(const char *server_address, int server_port, int proto, const char *send_buffer,
- char *recv_buffer, int recv_size) {
- int result;
- int sd;
+mp_state_enum process_request(const char *server_address, const int server_port, const int proto,
+ const char *send_buffer, char *recv_buffer, const int recv_size) {
- result = STATE_OK;
-
- result = np_net_connect(server_address, server_port, &sd, proto);
+ mp_state_enum result = STATE_OK;
+ int socket;
+ result = np_net_connect(server_address, server_port, &socket, proto);
if (result != STATE_OK) {
return STATE_CRITICAL;
}
- result = send_request(sd, proto, send_buffer, recv_buffer, recv_size);
+ result = send_request(socket, proto, send_buffer, recv_buffer, recv_size);
- close(sd);
+ close(socket);
return result;
}
/* opens a tcp or udp connection to a remote host or local socket */
-int np_net_connect(const char *host_name, int port, int *sd, int proto) {
+mp_state_enum np_net_connect(const char *host_name, int port, int *socketDescriptor,
+ const int proto) {
/* send back STATE_UNKOWN if there's an error
send back STATE_OK if we connect
send back STATE_CRITICAL if we can't connect.
Let upstream figure out what to send to the user. */
- struct addrinfo hints;
- struct addrinfo *r, *res;
- struct sockaddr_un su;
- char port_str[6], host[MAX_HOST_ADDRESS_LENGTH];
- size_t len;
- int socktype, result;
- short is_socket = (host_name[0] == '/');
-
- socktype = (proto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
+ bool is_socket = (host_name[0] == '/');
+ int socktype = (proto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
+ struct addrinfo hints = {};
+ struct addrinfo *res = NULL;
+ int result;
/* as long as it doesn't start with a '/', it's assumed a host or ip */
if (!is_socket) {
memset(&hints, 0, sizeof(hints));
@@ -177,38 +176,46 @@ int np_net_connect(const char *host_name, int port, int *sd, int proto) {
hints.ai_protocol = proto;
hints.ai_socktype = socktype;
- len = strlen(host_name);
+ size_t len = strlen(host_name);
/* check for an [IPv6] address (and strip the brackets) */
if (len >= 2 && host_name[0] == '[' && host_name[len - 1] == ']') {
host_name++;
len -= 2;
}
+
+ char host[MAX_HOST_ADDRESS_LENGTH];
+
if (len >= sizeof(host)) {
return STATE_UNKNOWN;
}
+
memcpy(host, host_name, len);
host[len] = '\0';
+
+ char port_str[6];
snprintf(port_str, sizeof(port_str), "%d", port);
- result = getaddrinfo(host, port_str, &hints, &res);
+ int getaddrinfo_err = getaddrinfo(host, port_str, &hints, &res);
- if (result != 0) {
+ if (getaddrinfo_err != 0) {
// printf("%s\n", gai_strerror(result));
return STATE_UNKNOWN;
}
- r = res;
- while (r) {
+ struct addrinfo *addressPointer = res;
+ while (addressPointer) {
/* attempt to create a socket */
- *sd = socket(r->ai_family, socktype, r->ai_protocol);
+ *socketDescriptor =
+ socket(addressPointer->ai_family, socktype, addressPointer->ai_protocol);
- if (*sd < 0) {
+ if (*socketDescriptor < 0) {
// printf("%s\n", _("Socket creation failed"));
- freeaddrinfo(r);
+ freeaddrinfo(addressPointer);
return STATE_UNKNOWN;
}
/* attempt to open a connection */
- result = connect(*sd, r->ai_addr, r->ai_addrlen);
+ result =
+ connect(*socketDescriptor, addressPointer->ai_addr, addressPointer->ai_addrlen);
if (result == 0) {
was_refused = false;
@@ -223,24 +230,28 @@ int np_net_connect(const char *host_name, int port, int *sd, int proto) {
}
}
- close(*sd);
- r = r->ai_next;
+ close(*socketDescriptor);
+ addressPointer = addressPointer->ai_next;
}
+
freeaddrinfo(res);
- }
- /* else the hostname is interpreted as a path to a unix socket */
- else {
+
+ } else {
+ /* else the hostname is interpreted as a path to a unix socket */
if (strlen(host_name) >= UNIX_PATH_MAX) {
die(STATE_UNKNOWN, _("Supplied path too long unix domain socket"));
}
- memset(&su, 0, sizeof(su));
+
+ struct sockaddr_un su = {};
su.sun_family = AF_UNIX;
strncpy(su.sun_path, host_name, UNIX_PATH_MAX);
- *sd = socket(PF_UNIX, SOCK_STREAM, 0);
- if (*sd < 0) {
+ *socketDescriptor = socket(PF_UNIX, SOCK_STREAM, 0);
+
+ if (*socketDescriptor < 0) {
die(STATE_UNKNOWN, _("Socket creation failed"));
}
- result = connect(*sd, (struct sockaddr *)&su, sizeof(su));
+
+ result = connect(*socketDescriptor, (struct sockaddr *)&su, sizeof(su));
if (result < 0 && errno == ECONNREFUSED) {
was_refused = true;
}
@@ -248,7 +259,9 @@ int np_net_connect(const char *host_name, int port, int *sd, int proto) {
if (result == 0) {
return STATE_OK;
- } else if (was_refused) {
+ }
+
+ if (was_refused) {
switch (econn_refuse_state) { /* a user-defined expected outcome */
case STATE_OK:
case STATE_WARNING: /* user wants WARN or OK on refusal, or... */
@@ -275,14 +288,11 @@ int np_net_connect(const char *host_name, int port, int *sd, int proto) {
}
}
-int send_request(int sd, int proto, const char *send_buffer, char *recv_buffer, int recv_size) {
- int result = STATE_OK;
- int send_result;
- int recv_result;
- struct timeval tv;
- fd_set readfds;
+mp_state_enum send_request(const int socket, const int proto, const char *send_buffer,
+ char *recv_buffer, const int recv_size) {
+ mp_state_enum result = STATE_OK;
- send_result = send(sd, send_buffer, strlen(send_buffer), 0);
+ ssize_t send_result = send(socket, send_buffer, strlen(send_buffer), 0);
if (send_result < 0 || (size_t)send_result != strlen(send_buffer)) {
// printf("%s\n", _("Send failed"));
result = STATE_WARNING;
@@ -290,21 +300,22 @@ int send_request(int sd, int proto, const char *send_buffer, char *recv_buffer,
/* wait up to the number of seconds for socket timeout minus one
for data from the host */
- tv.tv_sec = socket_timeout - 1;
- tv.tv_usec = 0;
+ struct timeval timestamp = {
+ .tv_sec = socket_timeout - 1,
+ .tv_usec = 0,
+ };
+ fd_set readfds;
FD_ZERO(&readfds);
- FD_SET(sd, &readfds);
- select(sd + 1, &readfds, NULL, NULL, &tv);
+ FD_SET(socket, &readfds);
+ select(socket + 1, &readfds, NULL, NULL, ×tamp);
/* make sure some data has arrived */
- if (!FD_ISSET(sd, &readfds)) {
+ if (!FD_ISSET(socket, &readfds)) {
strcpy(recv_buffer, "");
// printf("%s\n", _("No data was received from host!"));
result = STATE_WARNING;
- }
-
- else {
- recv_result = recv(sd, recv_buffer, (size_t)recv_size - 1, 0);
+ } else {
+ ssize_t recv_result = recv(socket, recv_buffer, (size_t)(recv_size - 1), 0);
if (recv_result == -1) {
strcpy(recv_buffer, "");
if (proto != IPPROTO_TCP) {
@@ -318,6 +329,7 @@ int send_request(int sd, int proto, const char *send_buffer, char *recv_buffer,
/* die returned string */
recv_buffer[recv_size - 1] = 0;
}
+
return result;
}
@@ -339,27 +351,27 @@ bool is_addr(const char *address) {
#ifdef USE_IPV6
if (address_family == AF_INET && is_inet_addr(address)) {
return true;
- } else if (address_family == AF_INET6 && is_inet6_addr(address)) {
+ }
+
+ if (address_family == AF_INET6 && is_inet6_addr(address)) {
return true;
}
#else
if (is_inet_addr(address)) {
- return (true);
+ return true;
}
#endif
- return (false);
+ return false;
}
-int dns_lookup(const char *in, struct sockaddr_storage *ss, int family) {
+bool dns_lookup(const char *node_string, struct sockaddr_storage *ss, const int family) {
struct addrinfo hints;
- struct addrinfo *res;
- int retval;
-
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = family;
- retval = getaddrinfo(in, NULL, &hints, &res);
+ struct addrinfo *res;
+ int retval = getaddrinfo(node_string, NULL, &hints, &res);
if (retval != 0) {
return false;
}
@@ -367,6 +379,8 @@ int dns_lookup(const char *in, struct sockaddr_storage *ss, int family) {
if (ss != NULL) {
memcpy(ss, res->ai_addr, res->ai_addrlen);
}
+
freeaddrinfo(res);
+
return true;
}
diff --git a/plugins/netutils.h b/plugins/netutils.h
index 6adb8e01..c4461113 100644
--- a/plugins/netutils.h
+++ b/plugins/netutils.h
@@ -31,7 +31,6 @@
#ifndef _NETUTILS_H_
#define _NETUTILS_H_
-#include "common.h"
#include "output.h"
#include "states.h"
#include "utils.h"
@@ -56,25 +55,26 @@
process_request(addr, port, IPPROTO_TCP, sbuf, rbuf, rsize)
#define process_udp_request(addr, port, sbuf, rbuf, rsize) \
process_request(addr, port, IPPROTO_UDP, sbuf, rbuf, rsize)
-int process_tcp_request2(const char *address, int port, const char *sbuffer, char *rbuffer,
- int rsize);
-int process_request(const char *address, int port, int proto, const char *sbuffer, char *rbuffer,
- int rsize);
+mp_state_enum process_tcp_request2(const char *server_address, int server_port,
+ const char *send_buffer, char *recv_buffer, int recv_size);
+mp_state_enum process_request(const char *server_address, int server_port, int proto,
+ const char *send_buffer, char *recv_buffer, int recv_size);
/* my_connect and wrapper macros */
#define my_tcp_connect(addr, port, s) np_net_connect(addr, port, s, IPPROTO_TCP)
#define my_udp_connect(addr, port, s) np_net_connect(addr, port, s, IPPROTO_UDP)
-int np_net_connect(const char *address, int port, int *sd, int proto);
+mp_state_enum np_net_connect(const char *host_name, int port, int *socketDescriptor, int proto);
/* send_request and wrapper macros */
#define send_tcp_request(s, sbuf, rbuf, rsize) send_request(s, IPPROTO_TCP, sbuf, rbuf, rsize)
#define send_udp_request(s, sbuf, rbuf, rsize) send_request(s, IPPROTO_UDP, sbuf, rbuf, rsize)
-int send_request(int sd, int proto, const char *send_buffer, char *recv_buffer, int recv_size);
+mp_state_enum send_request(int socket, int proto, const char *send_buffer, char *recv_buffer,
+ int recv_size);
/* "is_*" wrapper macros and functions */
bool is_host(const char *);
bool is_addr(const char *);
-int dns_lookup(const char *, struct sockaddr_storage *, int);
+bool dns_lookup(const char *, struct sockaddr_storage *, int);
void host_or_die(const char *str);
#define resolve_host_or_addr(addr, family) dns_lookup(addr, NULL, family)
#define is_inet_addr(addr) resolve_host_or_addr(addr, AF_INET)
@@ -86,8 +86,8 @@ void host_or_die(const char *str);
#endif
extern unsigned int socket_timeout;
-extern unsigned int socket_timeout_state;
-extern int econn_refuse_state;
+extern mp_state_enum socket_timeout_state;
+extern mp_state_enum econn_refuse_state;
extern bool was_refused;
extern int address_family;
@@ -106,12 +106,12 @@ void socket_timeout_alarm_handler(int) __attribute__((noreturn));
# define MP_TLSv1_1_OR_NEWER 9
# define MP_TLSv1_2_OR_NEWER 10
/* maybe this could be merged with the above np_net_connect, via some flags */
-int np_net_ssl_init(int sd);
-int np_net_ssl_init_with_hostname(int sd, char *host_name);
-int np_net_ssl_init_with_hostname_and_version(int sd, char *host_name, int version);
-int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int version, char *cert,
- char *privkey);
-void np_net_ssl_cleanup();
+int np_net_ssl_init(int socket);
+int np_net_ssl_init_with_hostname(int socket, char *host_name);
+int np_net_ssl_init_with_hostname_and_version(int socket, char *host_name, int version);
+int np_net_ssl_init_with_hostname_version_and_cert(int socket, char *host_name, int version,
+ char *cert, char *privkey);
+void np_net_ssl_cleanup(void);
int np_net_ssl_write(const void *buf, int num);
int np_net_ssl_read(void *buf, int num);
mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit);
--
cgit v1.2.3-74-g34f1
From 6bc9e518b247e85a39479a0ac6685e68c3a61b40 Mon Sep 17 00:00:00 2001
From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com>
Date: Sat, 8 Nov 2025 00:19:25 +0100
Subject: check_smtp: modern output + some tls cert helper functions
---
plugins/check_smtp.c | 676 ++++++++++++++++++++++++------------------
plugins/check_smtp.d/config.h | 16 +-
plugins/netutils.h | 20 ++
plugins/sslutils.c | 132 +++++++++
4 files changed, 550 insertions(+), 294 deletions(-)
(limited to 'plugins/netutils.h')
diff --git a/plugins/check_smtp.c b/plugins/check_smtp.c
index 83ad575c..f2c7f05c 100644
--- a/plugins/check_smtp.c
+++ b/plugins/check_smtp.c
@@ -28,20 +28,24 @@
*
*****************************************************************************/
-const char *progname = "check_smtp";
-const char *copyright = "2000-2024";
-const char *email = "devel@monitoring-plugins.org";
-
#include "common.h"
#include "netutils.h"
+#include "output.h"
+#include "perfdata.h"
+#include "thresholds.h"
#include "utils.h"
#include "base64.h"
#include "regex.h"
#include
+#include
#include "check_smtp.d/config.h"
#include "../lib/states.h"
+const char *progname = "check_smtp";
+const char *copyright = "2000-2024";
+const char *email = "devel@monitoring-plugins.org";
+
#define PROXY_PREFIX "PROXY TCP4 0.0.0.0 0.0.0.0 25 25\r\n"
#define SMTP_HELO "HELO "
#define SMTP_EHLO "EHLO "
@@ -161,323 +165,414 @@ int main(int argc, char **argv) {
gettimeofday(&start_time, NULL);
int socket_descriptor = 0;
+
/* try to connect to the host at the given port number */
- mp_state_enum result =
+ mp_state_enum tcp_result =
my_tcp_connect(config.server_address, config.server_port, &socket_descriptor);
- char *error_msg = "";
+ mp_check overall = mp_check_init();
+ mp_subcheck sc_tcp_connect = mp_subcheck_init();
char buffer[MAX_INPUT_BUFFER];
bool ssl_established = false;
- if (result == STATE_OK) { /* we connected */
- /* If requested, send PROXY header */
- if (config.use_proxy_prefix) {
- if (verbose) {
- printf("Sending header %s\n", PROXY_PREFIX);
- }
- my_send(config, PROXY_PREFIX, strlen(PROXY_PREFIX), socket_descriptor, ssl_established);
+
+ if (tcp_result != STATE_OK) {
+ // Connect failed
+ sc_tcp_connect = mp_set_subcheck_state(sc_tcp_connect, STATE_CRITICAL);
+ xasprintf(&sc_tcp_connect.output, "TCP connect to '%s' failed", config.server_address);
+ mp_add_subcheck_to_check(&overall, sc_tcp_connect);
+ mp_exit(overall);
+ }
+
+ /* we connected */
+ /* If requested, send PROXY header */
+ if (config.use_proxy_prefix) {
+ if (verbose) {
+ printf("Sending header %s\n", PROXY_PREFIX);
}
+ my_send(config, PROXY_PREFIX, strlen(PROXY_PREFIX), socket_descriptor, ssl_established);
+ }
#ifdef HAVE_SSL
- if (config.use_ssl) {
- result = np_net_ssl_init_with_hostname(socket_descriptor,
- (config.use_sni ? config.server_address : NULL));
- if (result != STATE_OK) {
- printf(_("CRITICAL - Cannot create SSL context.\n"));
- close(socket_descriptor);
- np_net_ssl_cleanup();
- exit(STATE_CRITICAL);
- }
- ssl_established = true;
+ if (config.use_ssl) {
+ int tls_result = np_net_ssl_init_with_hostname(
+ socket_descriptor, (config.use_sni ? config.server_address : NULL));
+
+ mp_subcheck sc_tls_connection = mp_subcheck_init();
+
+ if (tls_result != STATE_OK) {
+ close(socket_descriptor);
+ np_net_ssl_cleanup();
+
+ sc_tls_connection = mp_set_subcheck_state(sc_tls_connection, STATE_CRITICAL);
+ xasprintf(&sc_tls_connection.output, "cannot create TLS context");
+ mp_add_subcheck_to_check(&overall, sc_tls_connection);
+ mp_exit(overall);
}
+
+ sc_tls_connection = mp_set_subcheck_state(sc_tls_connection, STATE_OK);
+ xasprintf(&sc_tls_connection.output, "TLS context established");
+ mp_add_subcheck_to_check(&overall, sc_tls_connection);
+ ssl_established = true;
+ }
#endif
- /* watch for the SMTP connection string and */
- /* return a WARNING status if we couldn't read any data */
- if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) {
- printf(_("recv() failed\n"));
- exit(STATE_WARNING);
+ /* watch for the SMTP connection string and */
+ /* return a WARNING status if we couldn't read any data */
+ if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) {
+ mp_subcheck sc_read_data = mp_subcheck_init();
+ sc_read_data = mp_set_subcheck_state(sc_read_data, STATE_WARNING);
+ xasprintf(&sc_read_data.output, "recv() failed");
+ mp_add_subcheck_to_check(&overall, sc_read_data);
+ mp_exit(overall);
+ }
+
+ char *server_response = NULL;
+ /* save connect return (220 hostname ..) for later use */
+ xasprintf(&server_response, "%s", buffer);
+
+ /* send the HELO/EHLO command */
+ my_send(config, helocmd, (int)strlen(helocmd), socket_descriptor, ssl_established);
+
+ /* allow for response to helo command to reach us */
+ if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) {
+ mp_subcheck sc_read_data = mp_subcheck_init();
+ sc_read_data = mp_set_subcheck_state(sc_read_data, STATE_WARNING);
+ xasprintf(&sc_read_data.output, "recv() failed");
+ mp_add_subcheck_to_check(&overall, sc_read_data);
+ mp_exit(overall);
+ }
+
+ bool supports_tls = false;
+ if (config.use_ehlo || config.use_lhlo) {
+ if (strstr(buffer, "250 STARTTLS") != NULL || strstr(buffer, "250-STARTTLS") != NULL) {
+ supports_tls = true;
}
+ }
- char *server_response = NULL;
- /* save connect return (220 hostname ..) for later use */
- xasprintf(&server_response, "%s", buffer);
+ if (config.use_starttls && !supports_tls) {
+ smtp_quit(config, buffer, socket_descriptor, ssl_established);
- /* send the HELO/EHLO command */
- my_send(config, helocmd, (int)strlen(helocmd), socket_descriptor, ssl_established);
+ mp_subcheck sc_read_data = mp_subcheck_init();
+ sc_read_data = mp_set_subcheck_state(sc_read_data, STATE_WARNING);
+ xasprintf(&sc_read_data.output, "StartTLS not supported by server");
+ mp_add_subcheck_to_check(&overall, sc_read_data);
+ mp_exit(overall);
+ }
+
+#ifdef HAVE_SSL
+ if (config.use_starttls) {
+ /* send the STARTTLS command */
+ send(socket_descriptor, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0);
+
+ mp_subcheck sc_starttls_init = mp_subcheck_init();
+ recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
+ ssl_established); /* wait for it */
+ if (!strstr(buffer, SMTP_EXPECT)) {
+ smtp_quit(config, buffer, socket_descriptor, ssl_established);
+
+ xasprintf(&sc_starttls_init.output, "StartTLS not supported by server");
+ sc_starttls_init = mp_set_subcheck_state(sc_starttls_init, STATE_UNKNOWN);
+ mp_add_subcheck_to_check(&overall, sc_starttls_init);
+ mp_exit(overall);
+ }
+
+ mp_state_enum starttls_result = np_net_ssl_init_with_hostname(
+ socket_descriptor, (config.use_sni ? config.server_address : NULL));
+ if (starttls_result != STATE_OK) {
+ close(socket_descriptor);
+ np_net_ssl_cleanup();
+
+ sc_starttls_init = mp_set_subcheck_state(sc_starttls_init, STATE_CRITICAL);
+ xasprintf(&sc_starttls_init.output, "failed to create StartTLS context");
+ mp_add_subcheck_to_check(&overall, sc_starttls_init);
+ mp_exit(overall);
+ }
+ sc_starttls_init = mp_set_subcheck_state(sc_starttls_init, STATE_OK);
+ xasprintf(&sc_starttls_init.output, "created StartTLS context");
+ mp_add_subcheck_to_check(&overall, sc_starttls_init);
+
+ ssl_established = true;
+
+ /*
+ * Resend the EHLO command.
+ *
+ * RFC 3207 (4.2) says: ``The client MUST discard any knowledge
+ * obtained from the server, such as the list of SMTP service
+ * extensions, which was not obtained from the TLS negotiation
+ * itself. The client SHOULD send an EHLO command as the first
+ * command after a successful TLS negotiation.'' For this
+ * reason, some MTAs will not allow an AUTH LOGIN command before
+ * we resent EHLO via TLS.
+ */
+ if (my_send(config, helocmd, (int)strlen(helocmd), socket_descriptor, ssl_established) <=
+ 0) {
+ my_close(socket_descriptor);
+
+ mp_subcheck sc_ehlo = mp_subcheck_init();
+ sc_ehlo = mp_set_subcheck_state(sc_ehlo, STATE_UNKNOWN);
+ xasprintf(&sc_ehlo.output, "cannot send EHLO command via StartTLS");
+ mp_add_subcheck_to_check(&overall, sc_ehlo);
+ mp_exit(overall);
+ }
+
+ if (verbose) {
+ printf(_("sent %s"), helocmd);
+ }
- /* allow for response to helo command to reach us */
if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) {
- printf(_("recv() failed\n"));
- exit(STATE_WARNING);
+ my_close(socket_descriptor);
+
+ mp_subcheck sc_ehlo = mp_subcheck_init();
+ sc_ehlo = mp_set_subcheck_state(sc_ehlo, STATE_UNKNOWN);
+ xasprintf(&sc_ehlo.output, "cannot read EHLO response via StartTLS");
+ mp_add_subcheck_to_check(&overall, sc_ehlo);
+ mp_exit(overall);
}
- bool supports_tls = false;
- if (config.use_ehlo || config.use_lhlo) {
- if (strstr(buffer, "250 STARTTLS") != NULL || strstr(buffer, "250-STARTTLS") != NULL) {
- supports_tls = true;
- }
+ if (verbose) {
+ printf("%s", buffer);
}
+ }
- if (config.use_starttls && !supports_tls) {
- printf(_("WARNING - TLS not supported by server\n"));
+# ifdef USE_OPENSSL
+ if (ssl_established) {
+ net_ssl_check_cert_result cert_check_result =
+ np_net_ssl_check_cert2(config.days_till_exp_warn, config.days_till_exp_crit);
+
+ mp_subcheck sc_cert_check = mp_subcheck_init();
+
+ switch (cert_check_result.errors) {
+ case ALL_OK: {
+ xasprintf(&sc_cert_check.output, "Certificate expiration. Remaining time %g days",
+ cert_check_result.remaining_seconds / 86400);
+ sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state);
+ } break;
+ case NO_SERVER_CERTIFICATE_PRESENT: {
+ xasprintf(&sc_cert_check.output, "no server certificate present");
+ sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state);
+ } break;
+ case UNABLE_TO_RETRIEVE_CERTIFICATE_SUBJECT: {
+ xasprintf(&sc_cert_check.output, "can not retrieve certificate subject");
+ sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state);
+ } break;
+ case WRONG_TIME_FORMAT_IN_CERTIFICATE: {
+ xasprintf(&sc_cert_check.output, "wrong time format in certificate");
+ sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state);
+ } break;
+ };
+
+ mp_add_subcheck_to_check(&overall, sc_cert_check);
+
+ if (config.check_cert) {
smtp_quit(config, buffer, socket_descriptor, ssl_established);
- exit(STATE_WARNING);
+ my_close(socket_descriptor);
+ mp_exit(overall);
}
+ }
+# endif /* USE_OPENSSL */
-#ifdef HAVE_SSL
- if (config.use_starttls) {
- /* send the STARTTLS command */
- send(socket_descriptor, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0);
-
- recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
- ssl_established); /* wait for it */
- if (!strstr(buffer, SMTP_EXPECT)) {
- printf(_("Server does not support STARTTLS\n"));
- smtp_quit(config, buffer, socket_descriptor, ssl_established);
- exit(STATE_UNKNOWN);
- }
+#endif
- result = np_net_ssl_init_with_hostname(socket_descriptor,
- (config.use_sni ? config.server_address : NULL));
- if (result != STATE_OK) {
- printf(_("CRITICAL - Cannot create SSL context.\n"));
- close(socket_descriptor);
- np_net_ssl_cleanup();
- exit(STATE_CRITICAL);
- }
+ if (verbose) {
+ printf("%s", buffer);
+ }
- ssl_established = true;
-
- /*
- * Resend the EHLO command.
- *
- * RFC 3207 (4.2) says: ``The client MUST discard any knowledge
- * obtained from the server, such as the list of SMTP service
- * extensions, which was not obtained from the TLS negotiation
- * itself. The client SHOULD send an EHLO command as the first
- * command after a successful TLS negotiation.'' For this
- * reason, some MTAs will not allow an AUTH LOGIN command before
- * we resent EHLO via TLS.
- */
- if (my_send(config, helocmd, strlen(helocmd), socket_descriptor, ssl_established) <=
- 0) {
- printf("%s\n", _("SMTP UNKNOWN - Cannot send EHLO command via TLS."));
- my_close(socket_descriptor);
- exit(STATE_UNKNOWN);
- }
+ /* save buffer for later use */
+ xasprintf(&server_response, "%s%s", server_response, buffer);
+ /* strip the buffer of carriage returns */
+ strip(server_response);
- if (verbose) {
- printf(_("sent %s"), helocmd);
- }
+ /* make sure we find the droids we are looking for */
+ mp_subcheck sc_expect_response = mp_subcheck_init();
- if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <=
- 0) {
- printf("%s\n", _("SMTP UNKNOWN - Cannot read EHLO response via TLS."));
- my_close(socket_descriptor);
- exit(STATE_UNKNOWN);
- }
+ if (!strstr(server_response, config.server_expect)) {
+ sc_expect_response = mp_set_subcheck_state(sc_expect_response, STATE_WARNING);
+ if (config.server_port == SMTP_PORT) {
+ xasprintf(&sc_expect_response.output, _("invalid SMTP response received from host: %s"),
+ server_response);
+ } else {
+ xasprintf(&sc_expect_response.output,
+ _("invalid SMTP response received from host on port %d: %s"),
+ config.server_port, server_response);
+ }
+ exit(STATE_WARNING);
+ } else {
+ xasprintf(&sc_expect_response.output, "received valid SMTP response '%s' from host: '%s'",
+ config.server_expect, server_response);
+ sc_expect_response = mp_set_subcheck_state(sc_expect_response, STATE_OK);
+ }
- if (verbose) {
- printf("%s", buffer);
- }
+ mp_add_subcheck_to_check(&overall, sc_expect_response);
-# ifdef USE_OPENSSL
- if (config.check_cert) {
- result =
- np_net_ssl_check_cert(config.days_till_exp_warn, config.days_till_exp_crit);
- smtp_quit(config, buffer, socket_descriptor, ssl_established);
- my_close(socket_descriptor);
- exit(result);
- }
-# endif /* USE_OPENSSL */
+ if (config.send_mail_from) {
+ my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established);
+ if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >= 1 &&
+ verbose) {
+ printf("%s", buffer);
}
-#endif
+ }
- if (verbose) {
+ size_t counter = 0;
+ while (counter < config.ncommands) {
+ xasprintf(&cmd_str, "%s%s", config.commands[counter], "\r\n");
+ my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established);
+ if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >= 1 &&
+ verbose) {
printf("%s", buffer);
}
- /* save buffer for later use */
- xasprintf(&server_response, "%s%s", server_response, buffer);
- /* strip the buffer of carriage returns */
- strip(server_response);
+ strip(buffer);
- /* make sure we find the droids we are looking for */
- if (!strstr(server_response, config.server_expect)) {
- if (config.server_port == SMTP_PORT) {
- printf(_("Invalid SMTP response received from host: %s\n"), server_response);
- } else {
- printf(_("Invalid SMTP response received from host on port %d: %s\n"),
- config.server_port, server_response);
+ if (counter < config.nresponses) {
+ int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
+ regex_t preg;
+ int errcode = regcomp(&preg, config.responses[counter], cflags);
+ char errbuf[MAX_INPUT_BUFFER];
+ if (errcode != 0) {
+ regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER);
+ printf(_("Could Not Compile Regular Expression"));
+ exit(STATE_UNKNOWN);
}
- exit(STATE_WARNING);
- }
- if (config.send_mail_from) {
- my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established);
- if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >=
- 1 &&
- verbose) {
- printf("%s", buffer);
+ regmatch_t pmatch[10];
+ int eflags = 0;
+ int excode = regexec(&preg, buffer, 10, pmatch, eflags);
+ mp_subcheck sc_expected_responses = mp_subcheck_init();
+ if (excode == 0) {
+ xasprintf(&sc_expected_responses.output, "valid response '%s' to command '%s'",
+ buffer, config.commands[counter]);
+ sc_expected_responses = mp_set_subcheck_state(sc_expected_responses, STATE_OK);
+ } else if (excode == REG_NOMATCH) {
+ sc_expected_responses = mp_set_subcheck_state(sc_expected_responses, STATE_WARNING);
+ xasprintf(&sc_expected_responses.output, "invalid response '%s' to command '%s'",
+ buffer, config.commands[counter]);
+ } else {
+ regerror(excode, &preg, errbuf, MAX_INPUT_BUFFER);
+ xasprintf(&sc_expected_responses.output, "regexec execute error: %s", errbuf);
+ sc_expected_responses = mp_set_subcheck_state(sc_expected_responses, STATE_UNKNOWN);
}
}
+ counter++;
+ }
- int counter = 0;
- while (counter < config.ncommands) {
- xasprintf(&cmd_str, "%s%s", config.commands[counter], "\r\n");
- my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established);
- if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >=
- 1 &&
- verbose) {
- printf("%s", buffer);
- }
- strip(buffer);
- if (counter < config.nresponses) {
- int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
- regex_t preg;
- int errcode = regcomp(&preg, config.responses[counter], cflags);
- char errbuf[MAX_INPUT_BUFFER];
- if (errcode != 0) {
- regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER);
- printf(_("Could Not Compile Regular Expression"));
- exit(STATE_UNKNOWN);
+ if (config.authtype != NULL) {
+ mp_subcheck sc_auth = mp_subcheck_init();
+
+ if (strcmp(config.authtype, "LOGIN") == 0) {
+ char *abuf;
+ int ret;
+ do {
+ /* send AUTH LOGIN */
+ my_send(config, SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN), socket_descriptor,
+ ssl_established);
+
+ if (verbose) {
+ printf(_("sent %s\n"), "AUTH LOGIN");
}
- regmatch_t pmatch[10];
- int eflags = 0;
- int excode = regexec(&preg, buffer, 10, pmatch, eflags);
- if (excode == 0) {
- result = STATE_OK;
- } else if (excode == REG_NOMATCH) {
- result = STATE_WARNING;
- printf(_("SMTP %s - Invalid response '%s' to command '%s'\n"),
- state_text(result), buffer, config.commands[counter]);
- } else {
- regerror(excode, &preg, errbuf, MAX_INPUT_BUFFER);
- printf(_("Execute Error: %s\n"), errbuf);
- result = STATE_UNKNOWN;
+ if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
+ ssl_established)) <= 0) {
+ xasprintf(&sc_auth.output, _("recv() failed after AUTH LOGIN"));
+ sc_auth = mp_set_subcheck_state(sc_auth, STATE_WARNING);
+ break;
}
- }
- counter++;
- }
- if (config.authtype != NULL) {
- if (strcmp(config.authtype, "LOGIN") == 0) {
- char *abuf;
- int ret;
- do {
- if (config.authuser == NULL) {
- result = STATE_CRITICAL;
- xasprintf(&error_msg, _("no authuser specified, "));
- break;
- }
- if (config.authpass == NULL) {
- result = STATE_CRITICAL;
- xasprintf(&error_msg, _("no authpass specified, "));
- break;
- }
-
- /* send AUTH LOGIN */
- my_send(config, SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN), socket_descriptor,
- ssl_established);
- if (verbose) {
- printf(_("sent %s\n"), "AUTH LOGIN");
- }
-
- if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
- ssl_established)) <= 0) {
- xasprintf(&error_msg, _("recv() failed after AUTH LOGIN, "));
- result = STATE_WARNING;
- break;
- }
- if (verbose) {
- printf(_("received %s\n"), buffer);
- }
-
- if (strncmp(buffer, "334", 3) != 0) {
- result = STATE_CRITICAL;
- xasprintf(&error_msg, _("invalid response received after AUTH LOGIN, "));
- break;
- }
-
- /* encode authuser with base64 */
- base64_encode_alloc(config.authuser, strlen(config.authuser), &abuf);
- xasprintf(&abuf, "%s\r\n", abuf);
- my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
- if (verbose) {
- printf(_("sent %s\n"), abuf);
- }
-
- if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
- ssl_established)) <= 0) {
- result = STATE_CRITICAL;
- xasprintf(&error_msg, _("recv() failed after sending authuser, "));
- break;
- }
- if (verbose) {
- printf(_("received %s\n"), buffer);
- }
- if (strncmp(buffer, "334", 3) != 0) {
- result = STATE_CRITICAL;
- xasprintf(&error_msg, _("invalid response received after authuser, "));
- break;
- }
- /* encode authpass with base64 */
- base64_encode_alloc(config.authpass, strlen(config.authpass), &abuf);
- xasprintf(&abuf, "%s\r\n", abuf);
- my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
- if (verbose) {
- printf(_("sent %s\n"), abuf);
- }
- if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
- ssl_established)) <= 0) {
- result = STATE_CRITICAL;
- xasprintf(&error_msg, _("recv() failed after sending authpass, "));
- break;
- }
- if (verbose) {
- printf(_("received %s\n"), buffer);
- }
- if (strncmp(buffer, "235", 3) != 0) {
- result = STATE_CRITICAL;
- xasprintf(&error_msg, _("invalid response received after authpass, "));
- break;
- }
+ if (verbose) {
+ printf(_("received %s\n"), buffer);
+ }
+
+ if (strncmp(buffer, "334", 3) != 0) {
+ xasprintf(&sc_auth.output, "invalid response received after AUTH LOGIN");
+ sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
break;
- } while (false);
- } else {
- result = STATE_CRITICAL;
- xasprintf(&error_msg, _("only authtype LOGIN is supported, "));
- }
- }
+ }
- /* tell the server we're done */
- smtp_quit(config, buffer, socket_descriptor, ssl_established);
+ /* encode authuser with base64 */
+ base64_encode_alloc(config.authuser, strlen(config.authuser), &abuf);
+ xasprintf(&abuf, "%s\r\n", abuf);
+ my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
+ if (verbose) {
+ printf(_("sent %s\n"), abuf);
+ }
- /* finally close the connection */
- close(socket_descriptor);
+ if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
+ ssl_established)) <= 0) {
+ xasprintf(&sc_auth.output, "recv() failed after sending authuser");
+ sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
+ break;
+ }
+
+ if (verbose) {
+ printf(_("received %s\n"), buffer);
+ }
+
+ if (strncmp(buffer, "334", 3) != 0) {
+ xasprintf(&sc_auth.output, "invalid response received after authuser");
+ sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
+ break;
+ }
+
+ /* encode authpass with base64 */
+ base64_encode_alloc(config.authpass, strlen(config.authpass), &abuf);
+ xasprintf(&abuf, "%s\r\n", abuf);
+ my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
+
+ if (verbose) {
+ printf(_("sent %s\n"), abuf);
+ }
+
+ if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
+ ssl_established)) <= 0) {
+ xasprintf(&sc_auth.output, "recv() failed after sending authpass");
+ sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
+ break;
+ }
+
+ if (verbose) {
+ printf(_("received %s\n"), buffer);
+ }
+
+ if (strncmp(buffer, "235", 3) != 0) {
+ xasprintf(&sc_auth.output, "invalid response received after authpass");
+ sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
+ break;
+ }
+ break;
+ } while (false);
+ } else {
+ sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
+ xasprintf(&sc_auth.output, "only authtype LOGIN is supported");
+ }
+
+ mp_add_subcheck_to_check(&overall, sc_auth);
}
+ /* tell the server we're done */
+ smtp_quit(config, buffer, socket_descriptor, ssl_established);
+
+ /* finally close the connection */
+ close(socket_descriptor);
+
/* reset the alarm */
alarm(0);
long microsec = deltime(start_time);
double elapsed_time = (double)microsec / 1.0e6;
- if (result == STATE_OK) {
- if (config.check_critical_time && elapsed_time > config.critical_time) {
- result = STATE_CRITICAL;
- } else if (config.check_warning_time && elapsed_time > config.warning_time) {
- result = STATE_WARNING;
- }
- }
+ mp_perfdata pd_elapsed_time = perfdata_init();
+ pd_elapsed_time = mp_set_pd_value(pd_elapsed_time, elapsed_time);
+ pd_elapsed_time.label = "time";
+ pd_elapsed_time.uom = "s";
- printf(_("SMTP %s - %s%.3f sec. response time%s%s|%s\n"), state_text(result), error_msg,
- elapsed_time, verbose ? ", " : "", verbose ? buffer : "",
- fperfdata("time", elapsed_time, "s", config.check_warning_time, config.warning_time,
- config.check_critical_time, config.critical_time, true, 0, false, 0));
+ pd_elapsed_time = mp_pd_set_thresholds(pd_elapsed_time, config.connection_time);
- exit(result);
+ mp_subcheck sc_connection_time = mp_subcheck_init();
+ xasprintf(&sc_connection_time.output, "connection time: %.3gs", elapsed_time);
+ sc_connection_time =
+ mp_set_subcheck_state(sc_connection_time, mp_get_pd_status(pd_elapsed_time));
+ mp_add_subcheck_to_check(&overall, sc_connection_time);
+
+ mp_exit(overall);
}
/* process command-line arguments */
@@ -535,8 +630,8 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
}
}
- int command_size = 0;
- int response_size = 0;
+ unsigned long command_size = 0;
+ unsigned long response_size = 0;
bool implicit_tls = false;
int server_port_option = 0;
while (true) {
@@ -591,7 +686,7 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
result.config.commands =
realloc(result.config.commands, sizeof(char *) * command_size);
if (result.config.commands == NULL) {
- die(STATE_UNKNOWN, _("Could not realloc() units [%d]\n"),
+ die(STATE_UNKNOWN, _("Could not realloc() units [%lu]\n"),
result.config.ncommands);
}
}
@@ -605,7 +700,7 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
result.config.responses =
realloc(result.config.responses, sizeof(char *) * response_size);
if (result.config.responses == NULL) {
- die(STATE_UNKNOWN, _("Could not realloc() units [%d]\n"),
+ die(STATE_UNKNOWN, _("Could not realloc() units [%lu]\n"),
result.config.nresponses);
}
}
@@ -613,22 +708,22 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
strncpy(result.config.responses[result.config.nresponses], optarg, 255);
result.config.nresponses++;
break;
- case 'c': /* critical time threshold */
- if (!is_nonnegative(optarg)) {
- usage4(_("Critical time must be a positive"));
- } else {
- result.config.critical_time = strtod(optarg, NULL);
- result.config.check_critical_time = true;
+ case 'c': /* critical time threshold */ {
+ mp_range_parsed tmp = mp_parse_range_string(optarg);
+ if (tmp.error != MP_PARSING_SUCCES) {
+ die(STATE_UNKNOWN, "failed to parse critical time threshold");
}
- break;
- case 'w': /* warning time threshold */
- if (!is_nonnegative(optarg)) {
- usage4(_("Warning time must be a positive"));
- } else {
- result.config.warning_time = strtod(optarg, NULL);
- result.config.check_warning_time = true;
+ result.config.connection_time =
+ mp_thresholds_set_warn(result.config.connection_time, tmp.range);
+ } break;
+ case 'w': /* warning time threshold */ {
+ mp_range_parsed tmp = mp_parse_range_string(optarg);
+ if (tmp.error != MP_PARSING_SUCCES) {
+ die(STATE_UNKNOWN, "failed to parse warning time threshold");
}
- break;
+ result.config.connection_time =
+ mp_thresholds_set_crit(result.config.connection_time, tmp.range);
+ } break;
case 'v': /* verbose */
verbose++;
break;
@@ -742,6 +837,19 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
result.config.server_port = server_port_option;
}
+ if (result.config.authtype) {
+ if (strcmp(result.config.authtype, "LOGIN") == 0) {
+ if (result.config.authuser == NULL) {
+ usage4("no authuser specified");
+ }
+ if (result.config.authpass == NULL) {
+ usage4("no authpass specified");
+ }
+ } else {
+ usage4("only authtype LOGIN is supported");
+ }
+ }
+
return result;
}
@@ -791,7 +899,7 @@ char *smtp_quit(check_smtp_config config, char buffer[MAX_INPUT_BUFFER], int soc
int recvline(char *buf, size_t bufsize, check_smtp_config config, int socket_descriptor,
bool ssl_established) {
int result;
- int counter;
+ size_t counter;
for (counter = result = 0; counter < bufsize - 1; counter++) {
if ((result = my_recv(config, &buf[counter], 1, socket_descriptor, ssl_established)) != 1) {
@@ -799,7 +907,7 @@ int recvline(char *buf, size_t bufsize, check_smtp_config config, int socket_des
}
if (buf[counter] == '\n') {
buf[++counter] = '\0';
- return counter;
+ return (int)counter;
}
}
return (result == 1 || counter == 0) ? -2 : result; /* -2 if out of space */
diff --git a/plugins/check_smtp.d/config.h b/plugins/check_smtp.d/config.h
index 0a6511ef..bc433093 100644
--- a/plugins/check_smtp.d/config.h
+++ b/plugins/check_smtp.d/config.h
@@ -1,6 +1,7 @@
#pragma once
#include "../../config.h"
+#include "thresholds.h"
#include
#include
@@ -18,20 +19,18 @@ typedef struct {
char *server_expect;
bool ignore_send_quit_failure;
- double warning_time;
- bool check_warning_time;
- double critical_time;
- bool check_critical_time;
+ mp_thresholds connection_time;
+
bool use_ehlo;
bool use_lhlo;
char *from_arg;
bool send_mail_from;
- int ncommands;
+ unsigned long ncommands;
char **commands;
- int nresponses;
+ unsigned long nresponses;
char **responses;
char *authtype;
@@ -58,10 +57,7 @@ check_smtp_config check_smtp_config_init() {
.server_expect = SMTP_EXPECT,
.ignore_send_quit_failure = false,
- .warning_time = 0,
- .check_warning_time = false,
- .critical_time = 0,
- .check_critical_time = false,
+ .connection_time = mp_thresholds_init(),
.use_ehlo = false,
.use_lhlo = false,
diff --git a/plugins/netutils.h b/plugins/netutils.h
index c4461113..dbd22398 100644
--- a/plugins/netutils.h
+++ b/plugins/netutils.h
@@ -114,6 +114,26 @@ int np_net_ssl_init_with_hostname_version_and_cert(int socket, char *host_name,
void np_net_ssl_cleanup(void);
int np_net_ssl_write(const void *buf, int num);
int np_net_ssl_read(void *buf, int num);
+
+typedef enum {
+ ALL_OK,
+ NO_SERVER_CERTIFICATE_PRESENT,
+ UNABLE_TO_RETRIEVE_CERTIFICATE_SUBJECT,
+ WRONG_TIME_FORMAT_IN_CERTIFICATE,
+} retrieve_expiration_date_errors;
+
+typedef struct {
+ double remaining_seconds;
+ retrieve_expiration_date_errors errors;
+} retrieve_expiration_time_result;
+
+typedef struct {
+ mp_state_enum result_state;
+ double remaining_seconds;
+ retrieve_expiration_date_errors errors;
+} net_ssl_check_cert_result;
+net_ssl_check_cert_result np_net_ssl_check_cert2(int days_till_exp_warn, int days_till_exp_crit);
+
mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit);
mp_subcheck mp_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit);
#endif /* HAVE_SSL */
diff --git a/plugins/sslutils.c b/plugins/sslutils.c
index 0e6d7525..c1d15534 100644
--- a/plugins/sslutils.c
+++ b/plugins/sslutils.c
@@ -312,6 +312,138 @@ mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_
# endif /* USE_OPENSSL */
}
+retrieve_expiration_time_result np_net_ssl_get_cert_expiration(X509 *certificate) {
+# ifdef USE_OPENSSL
+ retrieve_expiration_time_result result = {
+ .errors = ALL_OK,
+ .remaining_seconds = {},
+ };
+
+ if (!certificate) {
+ // printf("%s\n", _("CRITICAL - No server certificate present to inspect."));
+ result.errors = NO_SERVER_CERTIFICATE_PRESENT;
+ return result;
+ }
+
+ /* Extract CN from certificate subject */
+ X509_NAME *subj = X509_get_subject_name(certificate);
+
+ if (!subj) {
+ // printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject."));
+ result.errors = UNABLE_TO_RETRIEVE_CERTIFICATE_SUBJECT;
+ return result;
+ }
+
+ char cn[MAX_CN_LENGTH] = "";
+ int cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, cn, sizeof(cn));
+ if (cnlen == -1) {
+ strcpy(cn, _("Unknown CN"));
+ }
+
+ /* Retrieve timestamp of certificate */
+ ASN1_STRING *expiration_timestamp = X509_get_notAfter(certificate);
+
+ int offset = 0;
+ struct tm stamp = {};
+ /* Generate tm structure to process timestamp */
+ if (expiration_timestamp->type == V_ASN1_UTCTIME) {
+ if (expiration_timestamp->length < 10) {
+ result.errors = WRONG_TIME_FORMAT_IN_CERTIFICATE;
+ return result;
+ }
+
+ stamp.tm_year =
+ (expiration_timestamp->data[0] - '0') * 10 + (expiration_timestamp->data[1] - '0');
+ if (stamp.tm_year < 50) {
+ stamp.tm_year += 100;
+ }
+ offset = 0;
+ } else {
+ if (expiration_timestamp->length < 12) {
+ result.errors = WRONG_TIME_FORMAT_IN_CERTIFICATE;
+ return result;
+ }
+
+ stamp.tm_year = (expiration_timestamp->data[0] - '0') * 1000 +
+ (expiration_timestamp->data[1] - '0') * 100 +
+ (expiration_timestamp->data[2] - '0') * 10 +
+ (expiration_timestamp->data[3] - '0');
+ stamp.tm_year -= 1900;
+ offset = 2;
+ }
+ stamp.tm_mon = (expiration_timestamp->data[2 + offset] - '0') * 10 +
+ (expiration_timestamp->data[3 + offset] - '0') - 1;
+ stamp.tm_mday = (expiration_timestamp->data[4 + offset] - '0') * 10 +
+ (expiration_timestamp->data[5 + offset] - '0');
+ stamp.tm_hour = (expiration_timestamp->data[6 + offset] - '0') * 10 +
+ (expiration_timestamp->data[7 + offset] - '0');
+ stamp.tm_min = (expiration_timestamp->data[8 + offset] - '0') * 10 +
+ (expiration_timestamp->data[9 + offset] - '0');
+ stamp.tm_sec = (expiration_timestamp->data[10 + offset] - '0') * 10 +
+ (expiration_timestamp->data[11 + offset] - '0');
+ stamp.tm_isdst = -1;
+
+ time_t tm_t = timegm(&stamp);
+ double time_left = difftime(tm_t, time(NULL));
+ result.remaining_seconds = time_left;
+
+ char *timezone = getenv("TZ");
+ setenv("TZ", "GMT", 1);
+ tzset();
+
+ char timestamp[50] = "";
+ strftime(timestamp, 50, "%c %z", localtime(&tm_t));
+ if (timezone) {
+ setenv("TZ", timezone, 1);
+ } else {
+ unsetenv("TZ");
+ }
+
+ tzset();
+
+ X509_free(certificate);
+
+ return result;
+# else /* ifndef USE_OPENSSL */
+ printf("%s\n", _("WARNING - Plugin does not support checking certificates."));
+ return STATE_WARNING;
+# endif /* USE_OPENSSL */
+}
+
+net_ssl_check_cert_result np_net_ssl_check_cert2(int days_till_exp_warn, int days_till_exp_crit) {
+# ifdef USE_OPENSSL
+ X509 *certificate = NULL;
+ certificate = SSL_get_peer_certificate(s);
+
+ retrieve_expiration_time_result expiration_date = np_net_ssl_get_cert_expiration(certificate);
+
+ net_ssl_check_cert_result result = {
+ .result_state = STATE_UNKNOWN,
+ .remaining_seconds = expiration_date.remaining_seconds,
+ .errors = expiration_date.errors,
+ };
+
+ if (expiration_date.errors == ALL_OK) {
+ // got a valid expiration date
+ unsigned int remaining_days = result.remaining_seconds / 86400;
+
+ if (remaining_days < days_till_exp_crit) {
+ result.result_state = STATE_CRITICAL;
+ } else if (remaining_days < days_till_exp_warn) {
+ result.result_state = STATE_WARNING;
+ } else {
+ result.result_state = STATE_OK;
+ }
+ }
+
+ return result;
+
+# else /* ifndef USE_OPENSSL */
+ printf("%s\n", _("WARNING - Plugin does not support checking certificates."));
+ return STATE_WARNING;
+# endif /* USE_OPENSSL */
+}
+
mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit) {
# ifdef USE_OPENSSL
X509 *certificate = NULL;
--
cgit v1.2.3-74-g34f1