From 0867319268898bc63f483e6257c65123e10603ba Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 9 Mar 2025 23:40:05 +0100 Subject: check_smtp: clang-format --- plugins/check_smtp.c | 850 +++++++++++++++++++++++++-------------------------- 1 file changed, 410 insertions(+), 440 deletions(-) diff --git a/plugins/check_smtp.c b/plugins/check_smtp.c index e6369e63..bb45e01b 100644 --- a/plugins/check_smtp.c +++ b/plugins/check_smtp.c @@ -1,32 +1,32 @@ /***************************************************************************** -* -* Monitoring check_smtp plugin -* -* License: GPL -* Copyright (c) 2000-2024 Monitoring Plugins Development Team -* -* Description: -* -* This file contains the check_smtp plugin -* -* This plugin will attempt to open an SMTP connection with the host. -* -* -* 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 check_smtp plugin + * + * License: GPL + * Copyright (c) 2000-2024 Monitoring Plugins Development Team + * + * Description: + * + * This file contains the check_smtp plugin + * + * This plugin will attempt to open an SMTP connection with the host. + * + * + * 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 . + * + * + *****************************************************************************/ const char *progname = "check_smtp"; const char *copyright = "2000-2024"; @@ -42,24 +42,20 @@ const char *email = "devel@monitoring-plugins.org"; #ifdef HAVE_SSL static bool check_cert = false; static int days_till_exp_warn, days_till_exp_crit; -# define my_recv(buf, len) (((use_starttls || use_ssl) && ssl_established) ? np_net_ssl_read(buf, len) : read(sd, buf, len)) -# define my_send(buf, len) (((use_starttls || use_ssl) && ssl_established) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0)) +# define my_recv(buf, len) (((use_starttls || use_ssl) && ssl_established) ? np_net_ssl_read(buf, len) : read(sd, buf, len)) +# define my_send(buf, len) (((use_starttls || use_ssl) && ssl_established) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0)) #else /* ifndef HAVE_SSL */ -# define my_recv(buf, len) read(sd, buf, len) -# define my_send(buf, len) send(sd, buf, len, 0) +# define my_recv(buf, len) read(sd, buf, len) +# define my_send(buf, len) send(sd, buf, len, 0) #endif -enum { - SMTP_PORT = 25, - SMTPS_PORT = 465 -}; -#define PROXY_PREFIX "PROXY TCP4 0.0.0.0 0.0.0.0 25 25\r\n" -#define SMTP_EXPECT "220" -#define SMTP_HELO "HELO " -#define SMTP_EHLO "EHLO " -#define SMTP_LHLO "LHLO " -#define SMTP_QUIT "QUIT\r\n" -#define SMTP_STARTTLS "STARTTLS\r\n" +#define PROXY_PREFIX "PROXY TCP4 0.0.0.0 0.0.0.0 25 25\r\n" +#define SMTP_EXPECT "220" +#define SMTP_HELO "HELO " +#define SMTP_EHLO "EHLO " +#define SMTP_LHLO "LHLO " +#define SMTP_QUIT "QUIT\r\n" +#define SMTP_STARTTLS "STARTTLS\r\n" #define SMTP_AUTH_LOGIN "AUTH LOGIN\r\n" #define EHLO_SUPPORTS_STARTTLS 1 @@ -81,17 +77,16 @@ static int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE; static int eflags = 0; static int errcode, excode; -static int server_port = SMTP_PORT; static int server_port_option = 0; static char *server_address = NULL; static char *server_expect = NULL; static char *mail_command = NULL; static char *from_arg = NULL; -static int send_mail_from=0; -static int ncommands=0; -static int command_size=0; -static int nresponses=0; -static int response_size=0; +static int send_mail_from = 0; +static int ncommands = 0; +static int command_size = 0; +static int nresponses = 0; +static int response_size = 0; static char **commands = NULL; static char **responses = NULL; static char *authtype = NULL; @@ -113,15 +108,12 @@ static char *localhostname = NULL; static int sd; static char buffer[MAX_INPUT_BUFFER]; enum { - TCP_PROTOCOL = 1, - UDP_PROTOCOL = 2, + TCP_PROTOCOL = 1, + UDP_PROTOCOL = 2, }; static bool ignore_send_quit_failure = false; - -int -main (int argc, char **argv) -{ +int main(int argc, char **argv) { bool supports_tls = false; int n = 0; double elapsed_time; @@ -134,63 +126,68 @@ main (int argc, char **argv) struct timeval tv; /* Catch pipe errors in read/write - sometimes occurs when writing QUIT */ - (void) signal (SIGPIPE, SIG_IGN); + (void)signal(SIGPIPE, SIG_IGN); - setlocale (LC_ALL, ""); - bindtextdomain (PACKAGE, LOCALEDIR); - textdomain (PACKAGE); + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); /* Parse extra opts if any */ - argv=np_extra_opts (&argc, argv, progname); + argv = np_extra_opts(&argc, argv, progname); - if (process_arguments (argc, argv) == ERROR) - usage4 (_("Could not parse arguments")); + if (process_arguments(argc, argv) == ERROR) { + usage4(_("Could not parse arguments")); + } /* If localhostname not set on command line, use gethostname to set */ - if(! localhostname){ - localhostname = malloc (HOST_MAX_BYTES); - if(!localhostname){ + if (!localhostname) { + localhostname = malloc(HOST_MAX_BYTES); + if (!localhostname) { printf(_("malloc() failed!\n")); return STATE_CRITICAL; } - if(gethostname(localhostname, HOST_MAX_BYTES)){ + if (gethostname(localhostname, HOST_MAX_BYTES)) { printf(_("gethostname() failed!\n")); return STATE_CRITICAL; } } - if(use_lhlo) - xasprintf (&helocmd, "%s%s%s", SMTP_LHLO, localhostname, "\r\n"); - else if(use_ehlo) - xasprintf (&helocmd, "%s%s%s", SMTP_EHLO, localhostname, "\r\n"); - else - xasprintf (&helocmd, "%s%s%s", SMTP_HELO, localhostname, "\r\n"); - - if (verbose) + if (use_lhlo) { + xasprintf(&helocmd, "%s%s%s", SMTP_LHLO, localhostname, "\r\n"); + } else if (use_ehlo) { + xasprintf(&helocmd, "%s%s%s", SMTP_EHLO, localhostname, "\r\n"); + } else { + xasprintf(&helocmd, "%s%s%s", SMTP_HELO, localhostname, "\r\n"); + } + + if (verbose) { printf("HELOCMD: %s", helocmd); + } /* initialize the MAIL command with optional FROM command */ - xasprintf (&cmd_str, "%sFROM:<%s>%s", mail_command, from_arg, "\r\n"); + xasprintf(&cmd_str, "%sFROM:<%s>%s", mail_command, from_arg, "\r\n"); - if (verbose && send_mail_from) - printf ("FROM CMD: %s", cmd_str); + if (verbose && send_mail_from) { + printf("FROM CMD: %s", cmd_str); + } /* initialize alarm signal handling */ - (void) signal (SIGALRM, socket_timeout_alarm_handler); + (void)signal(SIGALRM, socket_timeout_alarm_handler); /* set socket timeout */ - (void) alarm (socket_timeout); + (void)alarm(socket_timeout); /* start timer */ - gettimeofday (&tv, NULL); + gettimeofday(&tv, NULL); /* try to connect to the host at the given port number */ - result = my_tcp_connect (server_address, server_port, &sd); + result = my_tcp_connect(server_address, server_port, &sd); if (result == STATE_OK) { /* we connected */ /* If requested, send PROXY header */ if (use_proxy_prefix) { - if (verbose) - printf ("Sending header %s\n", PROXY_PREFIX); + if (verbose) { + printf("Sending header %s\n", PROXY_PREFIX); + } my_send(PROXY_PREFIX, strlen(PROXY_PREFIX)); } @@ -198,7 +195,7 @@ main (int argc, char **argv) if (use_ssl) { result = np_net_ssl_init_with_hostname(sd, (use_sni ? server_address : NULL)); if (result != STATE_OK) { - printf (_("CRITICAL - Cannot create SSL context.\n")); + printf(_("CRITICAL - Cannot create SSL context.\n")); close(sd); np_net_ssl_cleanup(); return STATE_CRITICAL; @@ -211,7 +208,7 @@ main (int argc, char **argv) /* watch for the SMTP connection string and */ /* return a WARNING status if we couldn't read any data */ if (recvlines(buffer, MAX_INPUT_BUFFER) <= 0) { - printf (_("recv() failed\n")); + printf(_("recv() failed\n")); return STATE_WARNING; } @@ -223,130 +220,131 @@ main (int argc, char **argv) /* allow for response to helo command to reach us */ if (recvlines(buffer, MAX_INPUT_BUFFER) <= 0) { - printf (_("recv() failed\n")); + printf(_("recv() failed\n")); return STATE_WARNING; - } else if(use_ehlo || use_lhlo){ - if(strstr(buffer, "250 STARTTLS") != NULL || - strstr(buffer, "250-STARTTLS") != NULL){ - supports_tls=true; + } else if (use_ehlo || use_lhlo) { + if (strstr(buffer, "250 STARTTLS") != NULL || strstr(buffer, "250-STARTTLS") != NULL) { + supports_tls = true; } } - if(use_starttls && ! supports_tls){ + if (use_starttls && !supports_tls) { printf(_("WARNING - TLS not supported by server\n")); smtp_quit(); return STATE_WARNING; } #ifdef HAVE_SSL - if(use_starttls) { - /* send the STARTTLS command */ - send(sd, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0); - - recvlines(buffer, MAX_INPUT_BUFFER); /* wait for it */ - if (!strstr (buffer, SMTP_EXPECT)) { - printf (_("Server does not support STARTTLS\n")); - smtp_quit(); - return STATE_UNKNOWN; - } - result = np_net_ssl_init_with_hostname(sd, (use_sni ? server_address : NULL)); - if(result != STATE_OK) { - printf (_("CRITICAL - Cannot create SSL context.\n")); - close(sd); - np_net_ssl_cleanup(); - return STATE_CRITICAL; - } else { - ssl_established = 1; - } - - /* - * 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(helocmd, strlen(helocmd)) <= 0) { - printf("%s\n", _("SMTP UNKNOWN - Cannot send EHLO command via TLS.")); - my_close(); - return STATE_UNKNOWN; - } - if (verbose) - printf(_("sent %s"), helocmd); - if ((n = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { - printf("%s\n", _("SMTP UNKNOWN - Cannot read EHLO response via TLS.")); - my_close(); - return STATE_UNKNOWN; - } - if (verbose) { - printf("%s", buffer); - } + if (use_starttls) { + /* send the STARTTLS command */ + send(sd, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0); + + recvlines(buffer, MAX_INPUT_BUFFER); /* wait for it */ + if (!strstr(buffer, SMTP_EXPECT)) { + printf(_("Server does not support STARTTLS\n")); + smtp_quit(); + return STATE_UNKNOWN; + } + result = np_net_ssl_init_with_hostname(sd, (use_sni ? server_address : NULL)); + if (result != STATE_OK) { + printf(_("CRITICAL - Cannot create SSL context.\n")); + close(sd); + np_net_ssl_cleanup(); + return STATE_CRITICAL; + } else { + ssl_established = 1; + } + + /* + * 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(helocmd, strlen(helocmd)) <= 0) { + printf("%s\n", _("SMTP UNKNOWN - Cannot send EHLO command via TLS.")); + my_close(); + return STATE_UNKNOWN; + } + if (verbose) { + printf(_("sent %s"), helocmd); + } + if ((n = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { + printf("%s\n", _("SMTP UNKNOWN - Cannot read EHLO response via TLS.")); + my_close(); + return STATE_UNKNOWN; + } + if (verbose) { + printf("%s", buffer); + } -# ifdef USE_OPENSSL - if ( check_cert ) { - result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit); - smtp_quit(); - my_close(); - return result; - } -# endif /* USE_OPENSSL */ +# ifdef USE_OPENSSL + if (check_cert) { + result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit); + smtp_quit(); + my_close(); + return result; + } +# endif /* USE_OPENSSL */ } #endif - if (verbose) - printf ("%s", buffer); + if (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(server_response); /* make sure we find the droids we are looking for */ - if (!strstr (server_response, server_expect)) { - if (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"), - server_port, server_response); + if (!strstr(server_response, server_expect)) { + if (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"), server_port, server_response); + } return STATE_WARNING; } if (send_mail_from) { - my_send(cmd_str, strlen(cmd_str)); - if (recvlines(buffer, MAX_INPUT_BUFFER) >= 1 && verbose) - printf("%s", buffer); + my_send(cmd_str, strlen(cmd_str)); + if (recvlines(buffer, MAX_INPUT_BUFFER) >= 1 && verbose) { + printf("%s", buffer); + } } n = 0; while (n < ncommands) { - xasprintf (&cmd_str, "%s%s", commands[n], "\r\n"); + xasprintf(&cmd_str, "%s%s", commands[n], "\r\n"); my_send(cmd_str, strlen(cmd_str)); - if (recvlines(buffer, MAX_INPUT_BUFFER) >= 1 && verbose) + if (recvlines(buffer, MAX_INPUT_BUFFER) >= 1 && verbose) { printf("%s", buffer); - strip (buffer); + } + strip(buffer); if (n < nresponses) { cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; - errcode = regcomp (&preg, responses[n], cflags); + errcode = regcomp(&preg, responses[n], cflags); if (errcode != 0) { - regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER); - printf (_("Could Not Compile Regular Expression")); + regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); + printf(_("Could Not Compile Regular Expression")); return ERROR; } - excode = regexec (&preg, buffer, 10, pmatch, eflags); + excode = regexec(&preg, buffer, 10, pmatch, eflags); if (excode == 0) { result = STATE_OK; - } - else if (excode == REG_NOMATCH) { + } else if (excode == REG_NOMATCH) { result = STATE_WARNING; - printf (_("SMTP %s - Invalid response '%s' to command '%s'\n"), state_text (result), buffer, commands[n]); - } - else { - regerror (excode, &preg, errbuf, MAX_INPUT_BUFFER); - printf (_("Execute Error: %s\n"), errbuf); + printf(_("SMTP %s - Invalid response '%s' to command '%s'\n"), state_text(result), buffer, commands[n]); + } else { + regerror(excode, &preg, errbuf, MAX_INPUT_BUFFER); + printf(_("Execute Error: %s\n"), errbuf); result = STATE_UNKNOWN; } } @@ -354,7 +352,7 @@ main (int argc, char **argv) } if (authtype != NULL) { - if (strcmp (authtype, "LOGIN") == 0) { + if (strcmp(authtype, "LOGIN") == 0) { char *abuf; int ret; do { @@ -371,29 +369,32 @@ main (int argc, char **argv) /* send AUTH LOGIN */ my_send(SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN)); - if (verbose) - printf (_("sent %s\n"), "AUTH LOGIN"); + if (verbose) { + printf(_("sent %s\n"), "AUTH LOGIN"); + } if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { xasprintf(&error_msg, _("recv() failed after AUTH LOGIN, ")); result = STATE_WARNING; break; } - if (verbose) - printf (_("received %s\n"), buffer); + if (verbose) { + printf(_("received %s\n"), buffer); + } - if (strncmp (buffer, "334", 3) != 0) { + 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 (authuser, strlen(authuser), &abuf); + base64_encode_alloc(authuser, strlen(authuser), &abuf); xasprintf(&abuf, "%s\r\n", abuf); my_send(abuf, strlen(abuf)); - if (verbose) - printf (_("sent %s\n"), abuf); + if (verbose) { + printf(_("sent %s\n"), abuf); + } if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { result = STATE_CRITICAL; @@ -401,19 +402,19 @@ main (int argc, char **argv) break; } if (verbose) { - printf (_("received %s\n"), buffer); + printf(_("received %s\n"), buffer); } - if (strncmp (buffer, "334", 3) != 0) { + 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 (authpass, strlen(authpass), &abuf); + base64_encode_alloc(authpass, strlen(authpass), &abuf); xasprintf(&abuf, "%s\r\n", abuf); my_send(abuf, strlen(abuf)); if (verbose) { - printf (_("sent %s\n"), abuf); + printf(_("sent %s\n"), abuf); } if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { result = STATE_CRITICAL; @@ -421,9 +422,9 @@ main (int argc, char **argv) break; } if (verbose) { - printf (_("received %s\n"), buffer); + printf(_("received %s\n"), buffer); } - if (strncmp (buffer, "235", 3) != 0) { + if (strncmp(buffer, "235", 3) != 0) { result = STATE_CRITICAL; xasprintf(&error_msg, _("invalid response received after authpass, ")); break; @@ -440,120 +441,112 @@ main (int argc, char **argv) smtp_quit(); /* finally close the connection */ - close (sd); + close(sd); } /* reset the alarm */ - alarm (0); + alarm(0); - microsec = deltime (tv); + microsec = deltime(tv); elapsed_time = (double)microsec / 1.0e6; if (result == STATE_OK) { - if (check_critical_time && elapsed_time > critical_time) + if (check_critical_time && elapsed_time > critical_time) { result = STATE_CRITICAL; - else if (check_warning_time && elapsed_time > warning_time) + } else if (check_warning_time && elapsed_time > warning_time) { result = STATE_WARNING; + } } - 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", - (int)check_warning_time, warning_time, - (int)check_critical_time, critical_time, - true, 0, false, 0)); + 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", (int)check_warning_time, warning_time, (int)check_critical_time, critical_time, true, 0, + false, 0)); return result; } - - /* process command-line arguments */ -int -process_arguments (int argc, char **argv) -{ +int process_arguments(int argc, char **argv) { int c; - char* temp; + char *temp; bool implicit_tls = false; enum { - SNI_OPTION + SNI_OPTION }; int option = 0; - static struct option longopts[] = { - {"hostname", required_argument, 0, 'H'}, - {"expect", required_argument, 0, 'e'}, - {"critical", required_argument, 0, 'c'}, - {"warning", required_argument, 0, 'w'}, - {"timeout", required_argument, 0, 't'}, - {"port", required_argument, 0, 'p'}, - {"from", required_argument, 0, 'f'}, - {"fqdn", required_argument, 0, 'F'}, - {"authtype", required_argument, 0, 'A'}, - {"authuser", required_argument, 0, 'U'}, - {"authpass", required_argument, 0, 'P'}, - {"command", required_argument, 0, 'C'}, - {"response", required_argument, 0, 'R'}, - {"verbose", no_argument, 0, 'v'}, - {"version", no_argument, 0, 'V'}, - {"use-ipv4", no_argument, 0, '4'}, - {"use-ipv6", no_argument, 0, '6'}, - {"help", no_argument, 0, 'h'}, - {"lmtp", no_argument, 0, 'L'}, - {"ssl", no_argument, 0, 's'}, - {"tls", no_argument, 0, 's'}, - {"starttls",no_argument,0,'S'}, - {"sni", no_argument, 0, SNI_OPTION}, - {"certificate",required_argument,0,'D'}, - {"ignore-quit-failure",no_argument,0,'q'}, - {"proxy",no_argument,0,'r'}, - {0, 0, 0, 0} - }; - - if (argc < 2) + static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, + {"expect", required_argument, 0, 'e'}, + {"critical", required_argument, 0, 'c'}, + {"warning", required_argument, 0, 'w'}, + {"timeout", required_argument, 0, 't'}, + {"port", required_argument, 0, 'p'}, + {"from", required_argument, 0, 'f'}, + {"fqdn", required_argument, 0, 'F'}, + {"authtype", required_argument, 0, 'A'}, + {"authuser", required_argument, 0, 'U'}, + {"authpass", required_argument, 0, 'P'}, + {"command", required_argument, 0, 'C'}, + {"response", required_argument, 0, 'R'}, + {"verbose", no_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, + {"use-ipv4", no_argument, 0, '4'}, + {"use-ipv6", no_argument, 0, '6'}, + {"help", no_argument, 0, 'h'}, + {"lmtp", no_argument, 0, 'L'}, + {"ssl", no_argument, 0, 's'}, + {"tls", no_argument, 0, 's'}, + {"starttls", no_argument, 0, 'S'}, + {"sni", no_argument, 0, SNI_OPTION}, + {"certificate", required_argument, 0, 'D'}, + {"ignore-quit-failure", no_argument, 0, 'q'}, + {"proxy", no_argument, 0, 'r'}, + {0, 0, 0, 0}}; + + if (argc < 2) { return ERROR; + } for (c = 1; c < argc; c++) { - if (strcmp ("-to", argv[c]) == 0) - strcpy (argv[c], "-t"); - else if (strcmp ("-wt", argv[c]) == 0) - strcpy (argv[c], "-w"); - else if (strcmp ("-ct", argv[c]) == 0) - strcpy (argv[c], "-c"); + if (strcmp("-to", argv[c]) == 0) { + strcpy(argv[c], "-t"); + } else if (strcmp("-wt", argv[c]) == 0) { + strcpy(argv[c], "-w"); + } else if (strcmp("-ct", argv[c]) == 0) { + strcpy(argv[c], "-c"); + } } while (1) { - c = getopt_long (argc, argv, "+hVv46Lrt:p:f:e:c:w:H:C:R:sSD:F:A:U:P:q", - longopts, &option); + c = getopt_long(argc, argv, "+hVv46Lrt:p:f:e:c:w:H:C:R:sSD:F:A:U:P:q", longopts, &option); - if (c == -1 || c == EOF) + if (c == -1 || c == EOF) { break; + } switch (c) { - case 'H': /* hostname */ - if (is_host (optarg)) { + case 'H': /* hostname */ + if (is_host(optarg)) { server_address = optarg; - } - else { - usage2 (_("Invalid hostname/address"), optarg); + } else { + usage2(_("Invalid hostname/address"), optarg); } break; - case 'p': /* port */ - if (is_intpos (optarg)) - server_port_option = atoi (optarg); - else - usage4 (_("Port must be a positive integer")); + case 'p': /* port */ + if (is_intpos(optarg)) { + server_port_option = atoi(optarg); + } else { + usage4(_("Port must be a positive integer")); + } break; case 'F': - /* localhostname */ + /* localhostname */ localhostname = strdup(optarg); break; - case 'f': /* from argument */ + case 'f': /* from argument */ from_arg = optarg + strspn(optarg, "<"); from_arg = strndup(from_arg, strcspn(from_arg, ">")); send_mail_from = 1; @@ -568,97 +561,98 @@ process_arguments (int argc, char **argv) case 'P': authpass = optarg; break; - case 'e': /* server expect string on 220 */ + case 'e': /* server expect string on 220 */ server_expect = optarg; break; - case 'C': /* commands */ + case 'C': /* commands */ if (ncommands >= command_size) { - command_size+=8; - commands = realloc (commands, sizeof(char *) * command_size); - if (commands == NULL) - die (STATE_UNKNOWN, - _("Could not realloc() units [%d]\n"), ncommands); + command_size += 8; + commands = realloc(commands, sizeof(char *) * command_size); + if (commands == NULL) { + die(STATE_UNKNOWN, _("Could not realloc() units [%d]\n"), ncommands); + } } - commands[ncommands] = (char *) malloc (sizeof(char) * 255); - strncpy (commands[ncommands], optarg, 255); + commands[ncommands] = (char *)malloc(sizeof(char) * 255); + strncpy(commands[ncommands], optarg, 255); ncommands++; break; - case 'R': /* server responses */ + case 'R': /* server responses */ if (nresponses >= response_size) { response_size += 8; - responses = realloc (responses, sizeof(char *) * response_size); - if (responses == NULL) - die (STATE_UNKNOWN, - _("Could not realloc() units [%d]\n"), nresponses); + responses = realloc(responses, sizeof(char *) * response_size); + if (responses == NULL) { + die(STATE_UNKNOWN, _("Could not realloc() units [%d]\n"), nresponses); + } } - responses[nresponses] = (char *) malloc (sizeof(char) * 255); - strncpy (responses[nresponses], optarg, 255); + responses[nresponses] = (char *)malloc(sizeof(char) * 255); + strncpy(responses[nresponses], optarg, 255); nresponses++; break; - case 'c': /* critical time threshold */ - if (!is_nonnegative (optarg)) - usage4 (_("Critical time must be a positive")); - else { - critical_time = strtod (optarg, NULL); + case 'c': /* critical time threshold */ + if (!is_nonnegative(optarg)) { + usage4(_("Critical time must be a positive")); + } else { + critical_time = strtod(optarg, NULL); check_critical_time = true; } break; - case 'w': /* warning time threshold */ - if (!is_nonnegative (optarg)) - usage4 (_("Warning time must be a positive")); - else { - warning_time = strtod (optarg, NULL); + case 'w': /* warning time threshold */ + if (!is_nonnegative(optarg)) { + usage4(_("Warning time must be a positive")); + } else { + warning_time = strtod(optarg, NULL); check_warning_time = true; } break; - case 'v': /* verbose */ + case 'v': /* verbose */ verbose++; break; case 'q': - ignore_send_quit_failure = true; /* ignore problem sending QUIT */ + ignore_send_quit_failure = true; /* ignore problem sending QUIT */ break; - case 't': /* timeout */ - if (is_intnonneg (optarg)) { - socket_timeout = atoi (optarg); - } - else { - usage4 (_("Timeout interval must be a positive integer")); + case 't': /* timeout */ + if (is_intnonneg(optarg)) { + socket_timeout = atoi(optarg); + } else { + usage4(_("Timeout interval must be a positive integer")); } break; case 'D': - /* Check SSL cert validity */ + /* Check SSL cert validity */ #ifdef USE_OPENSSL - if ((temp=strchr(optarg,','))!=NULL) { - *temp='\0'; - if (!is_intnonneg (optarg)) - usage2 ("Invalid certificate expiration period", optarg); - days_till_exp_warn = atoi(optarg); - *temp=','; - temp++; - if (!is_intnonneg (temp)) - usage2 (_("Invalid certificate expiration period"), temp); - days_till_exp_crit = atoi (temp); - } - else { - days_till_exp_crit=0; - if (!is_intnonneg (optarg)) - usage2 ("Invalid certificate expiration period", optarg); - days_till_exp_warn = atoi (optarg); - } + if ((temp = strchr(optarg, ',')) != NULL) { + *temp = '\0'; + if (!is_intnonneg(optarg)) { + usage2("Invalid certificate expiration period", optarg); + } + days_till_exp_warn = atoi(optarg); + *temp = ','; + temp++; + if (!is_intnonneg(temp)) { + usage2(_("Invalid certificate expiration period"), temp); + } + days_till_exp_crit = atoi(temp); + } else { + days_till_exp_crit = 0; + if (!is_intnonneg(optarg)) { + usage2("Invalid certificate expiration period", optarg); + } + days_till_exp_warn = atoi(optarg); + } check_cert = true; ignore_send_quit_failure = true; #else - usage (_("SSL support not available - install OpenSSL and recompile")); + usage(_("SSL support not available - install OpenSSL and recompile")); #endif implicit_tls = true; // fallthrough case 's': - /* ssl */ + /* ssl */ use_ssl = true; server_port = SMTPS_PORT; break; case 'S': - /* starttls */ + /* starttls */ use_starttls = true; use_ehlo = true; break; @@ -666,7 +660,7 @@ process_arguments (int argc, char **argv) #ifdef HAVE_SSL use_sni = true; #else - usage (_("SSL support not available - install OpenSSL and recompile")); + usage(_("SSL support not available - install OpenSSL and recompile")); #endif break; case 'r': @@ -682,48 +676,51 @@ process_arguments (int argc, char **argv) #ifdef USE_IPV6 address_family = AF_INET6; #else - usage4 (_("IPv6 support not available")); + usage4(_("IPv6 support not available")); #endif break; - case 'V': /* version */ - print_revision (progname, NP_VERSION); - exit (STATE_UNKNOWN); - case 'h': /* help */ - print_help (); - exit (STATE_UNKNOWN); - case '?': /* help */ - usage5 (); + case 'V': /* version */ + print_revision(progname, NP_VERSION); + exit(STATE_UNKNOWN); + case 'h': /* help */ + print_help(); + exit(STATE_UNKNOWN); + case '?': /* help */ + usage5(); } } c = optind; if (server_address == NULL) { if (argv[c]) { - if (is_host (argv[c])) + if (is_host(argv[c])) { server_address = argv[c]; - else - usage2 (_("Invalid hostname/address"), argv[c]); - } - else { - xasprintf (&server_address, "127.0.0.1"); + } else { + usage2(_("Invalid hostname/address"), argv[c]); + } + } else { + xasprintf(&server_address, "127.0.0.1"); } } - if (server_expect == NULL) - server_expect = strdup (SMTP_EXPECT); + if (server_expect == NULL) { + server_expect = strdup(SMTP_EXPECT); + } - if (mail_command == NULL) + if (mail_command == NULL) { mail_command = strdup("MAIL "); + } - if (from_arg==NULL) + if (from_arg == NULL) { from_arg = strdup(" "); + } if (use_starttls && use_ssl) { if (implicit_tls) { use_ssl = false; server_port = SMTP_PORT; } else { - usage4 (_("Set either -s/--ssl/--tls or -S/--starttls")); + usage4(_("Set either -s/--ssl/--tls or -S/--starttls")); } } @@ -731,54 +728,44 @@ process_arguments (int argc, char **argv) server_port = server_port_option; } - return validate_arguments (); -} - - - -int -validate_arguments (void) -{ - return OK; + return validate_arguments(); } +int validate_arguments(void) { return OK; } -void -smtp_quit(void) -{ +void smtp_quit(void) { int bytes; int n; n = my_send(SMTP_QUIT, strlen(SMTP_QUIT)); - if(n < 0) { - if(ignore_send_quit_failure) { - if(verbose) { + if (n < 0) { + if (ignore_send_quit_failure) { + if (verbose) { printf(_("Connection closed by server before sending QUIT command\n")); } return; } - die (STATE_UNKNOWN, - _("Connection closed by server before sending QUIT command\n")); + die(STATE_UNKNOWN, _("Connection closed by server before sending QUIT command\n")); } - if (verbose) + if (verbose) { printf(_("sent %s\n"), "QUIT"); + } /* read the response but don't care about problems */ bytes = recvlines(buffer, MAX_INPUT_BUFFER); if (verbose) { - if (bytes < 0) + if (bytes < 0) { printf(_("recv() failed after QUIT.")); - else if (bytes == 0) + } else if (bytes == 0) { printf(_("Connection reset by peer.")); - else { + } else { buffer[bytes] = '\0'; printf(_("received %s\n"), buffer); } } } - /* * Receive one line, copy it into buf and nul-terminate it. Returns the * number of bytes written to buf (excluding the '\0') or 0 on EOF or <0 on @@ -788,24 +775,22 @@ smtp_quit(void) * function which buffers the data, move that to netutils.c and change * check_smtp and other plugins to use that. Also, remove (\r)\n. */ -int -recvline(char *buf, size_t bufsize) -{ +int recvline(char *buf, size_t bufsize) { int result; unsigned i; for (i = result = 0; i < bufsize - 1; i++) { - if ((result = my_recv(&buf[i], 1)) != 1) + if ((result = my_recv(&buf[i], 1)) != 1) { break; + } if (buf[i] == '\n') { buf[++i] = '\0'; return i; } } - return (result == 1 || i == 0) ? -2 : result; /* -2 if out of space */ + return (result == 1 || i == 0) ? -2 : result; /* -2 if out of space */ } - /* * Receive one or more lines, copy them into buf and nul-terminate it. Returns * the number of bytes written to buf (excluding the '\0') or 0 on EOF or <0 on @@ -820,26 +805,20 @@ recvline(char *buf, size_t bufsize) * * TODO: Move this to netutils.c. Also, remove \r and possibly the final \n. */ -int -recvlines(char *buf, size_t bufsize) -{ +int recvlines(char *buf, size_t bufsize) { int result, i; - for (i = 0; /* forever */; i += result) - if (!((result = recvline(buf + i, bufsize - i)) > 3 && - isdigit((int)buf[i]) && - isdigit((int)buf[i + 1]) && - isdigit((int)buf[i + 2]) && - buf[i + 3] == '-')) + for (i = 0; /* forever */; i += result) { + if (!((result = recvline(buf + i, bufsize - i)) > 3 && isdigit((int)buf[i]) && isdigit((int)buf[i + 1]) && + isdigit((int)buf[i + 2]) && buf[i + 3] == '-')) { break; + } + } return (result <= 0) ? result : result + i; } - -int -my_close (void) -{ +int my_close(void) { int result; result = close(sd); #ifdef HAVE_SSL @@ -848,89 +827,80 @@ my_close (void) return result; } - -void -print_help (void) -{ +void print_help(void) { char *myport; - xasprintf (&myport, "%d", SMTP_PORT); + xasprintf(&myport, "%d", SMTP_PORT); - print_revision (progname, NP_VERSION); + print_revision(progname, NP_VERSION); - printf ("Copyright (c) 1999-2001 Ethan Galstad \n"); - printf (COPYRIGHT, copyright, email); + printf("Copyright (c) 1999-2001 Ethan Galstad \n"); + printf(COPYRIGHT, copyright, email); printf("%s\n", _("This plugin will attempt to open an SMTP connection with the host.")); - printf ("\n\n"); + printf("\n\n"); - print_usage (); + print_usage(); - printf (UT_HELP_VRSN); - printf (UT_EXTRA_OPTS); + printf(UT_HELP_VRSN); + printf(UT_EXTRA_OPTS); - printf (UT_HOST_PORT, 'p', myport); + printf(UT_HOST_PORT, 'p', myport); - printf (UT_IPv46); + printf(UT_IPv46); - printf (" %s\n", "-e, --expect=STRING"); - printf (_(" String to expect in first line of server response (default: '%s')\n"), SMTP_EXPECT); - printf (" %s\n", "-C, --command=STRING"); - printf (" %s\n", _("SMTP command (may be used repeatedly)")); - printf (" %s\n", "-R, --response=STRING"); - printf (" %s\n", _("Expected response to command (may be used repeatedly)")); - printf (" %s\n", "-f, --from=STRING"); - printf (" %s\n", _("FROM-address to include in MAIL command, required by Exchange 2000")), - printf (" %s\n", "-F, --fqdn=STRING"); - printf (" %s\n", _("FQDN used for HELO")); - printf (" %s\n", "-r, --proxy"); - printf (" %s\n", _("Use PROXY protocol prefix for the connection.")); + printf(" %s\n", "-e, --expect=STRING"); + printf(_(" String to expect in first line of server response (default: '%s')\n"), SMTP_EXPECT); + printf(" %s\n", "-C, --command=STRING"); + printf(" %s\n", _("SMTP command (may be used repeatedly)")); + printf(" %s\n", "-R, --response=STRING"); + printf(" %s\n", _("Expected response to command (may be used repeatedly)")); + printf(" %s\n", "-f, --from=STRING"); + printf(" %s\n", _("FROM-address to include in MAIL command, required by Exchange 2000")), printf(" %s\n", "-F, --fqdn=STRING"); + printf(" %s\n", _("FQDN used for HELO")); + printf(" %s\n", "-r, --proxy"); + printf(" %s\n", _("Use PROXY protocol prefix for the connection.")); #ifdef HAVE_SSL - printf (" %s\n", "-D, --certificate=INTEGER[,INTEGER]"); - printf (" %s\n", _("Minimum number of days a certificate has to be valid.")); - printf (" %s\n", "-s, --ssl, --tls"); - printf (" %s\n", _("Use SSL/TLS for the connection.")); - printf (_(" Sets default port to %d.\n"), SMTPS_PORT); - printf (" %s\n", "-S, --starttls"); - printf (" %s\n", _("Use STARTTLS for the connection.")); - printf (" %s\n", "--sni"); - printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)")); + printf(" %s\n", "-D, --certificate=INTEGER[,INTEGER]"); + printf(" %s\n", _("Minimum number of days a certificate has to be valid.")); + printf(" %s\n", "-s, --ssl, --tls"); + printf(" %s\n", _("Use SSL/TLS for the connection.")); + printf(_(" Sets default port to %d.\n"), SMTPS_PORT); + printf(" %s\n", "-S, --starttls"); + printf(" %s\n", _("Use STARTTLS for the connection.")); + printf(" %s\n", "--sni"); + printf(" %s\n", _("Enable SSL/TLS hostname extension support (SNI)")); #endif - printf (" %s\n", "-A, --authtype=STRING"); - printf (" %s\n", _("SMTP AUTH type to check (default none, only LOGIN supported)")); - printf (" %s\n", "-U, --authuser=STRING"); - printf (" %s\n", _("SMTP AUTH username")); - printf (" %s\n", "-P, --authpass=STRING"); - printf (" %s\n", _("SMTP AUTH password")); - printf (" %s\n", "-L, --lmtp"); - printf (" %s\n", _("Send LHLO instead of HELO/EHLO")); - printf (" %s\n", "-q, --ignore-quit-failure"); - printf (" %s\n", _("Ignore failure when sending QUIT command to server")); - - printf (UT_WARN_CRIT); + printf(" %s\n", "-A, --authtype=STRING"); + printf(" %s\n", _("SMTP AUTH type to check (default none, only LOGIN supported)")); + printf(" %s\n", "-U, --authuser=STRING"); + printf(" %s\n", _("SMTP AUTH username")); + printf(" %s\n", "-P, --authpass=STRING"); + printf(" %s\n", _("SMTP AUTH password")); + printf(" %s\n", "-L, --lmtp"); + printf(" %s\n", _("Send LHLO instead of HELO/EHLO")); + printf(" %s\n", "-q, --ignore-quit-failure"); + printf(" %s\n", _("Ignore failure when sending QUIT command to server")); + + printf(UT_WARN_CRIT); - printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); + printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); - printf (UT_VERBOSE); + printf(UT_VERBOSE); printf("\n"); - printf ("%s\n", _("Successful connects return STATE_OK, refusals and timeouts return")); - printf ("%s\n", _("STATE_CRITICAL, other errors return STATE_UNKNOWN. Successful")); - printf ("%s\n", _("connects, but incorrect response messages from the host result in")); - printf ("%s\n", _("STATE_WARNING return values.")); + printf("%s\n", _("Successful connects return STATE_OK, refusals and timeouts return")); + printf("%s\n", _("STATE_CRITICAL, other errors return STATE_UNKNOWN. Successful")); + printf("%s\n", _("connects, but incorrect response messages from the host result in")); + printf("%s\n", _("STATE_WARNING return values.")); - printf (UT_SUPPORT); + printf(UT_SUPPORT); } - - -void -print_usage (void) -{ - printf ("%s\n", _("Usage:")); - printf ("%s -H host [-p port] [-4|-6] [-e expect] [-C command] [-R response] [-f from addr]\n", progname); - printf ("[-A authtype -U authuser -P authpass] [-w warn] [-c crit] [-t timeout] [-q]\n"); - printf ("[-F fqdn] [-S] [-L] [-D warn days cert expire[,crit days cert expire]] [-r] [--sni] [-v] \n"); +void print_usage(void) { + printf("%s\n", _("Usage:")); + printf("%s -H host [-p port] [-4|-6] [-e expect] [-C command] [-R response] [-f from addr]\n", progname); + printf("[-A authtype -U authuser -P authpass] [-w warn] [-c crit] [-t timeout] [-q]\n"); + printf("[-F fqdn] [-S] [-L] [-D warn days cert expire[,crit days cert expire]] [-r] [--sni] [-v] \n"); } - -- cgit v1.2.3-74-g34f1 From 883fcb607f9b3e810e22b2e21da7441a073b6e32 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 10 Mar 2025 01:31:52 +0100 Subject: Refactor check_smtp --- plugins/Makefile.am | 3 +- plugins/check_smtp.c | 518 ++++++++++++++++++++---------------------- plugins/check_smtp.d/config.h | 92 ++++++++ 3 files changed, 343 insertions(+), 270 deletions(-) create mode 100644 plugins/check_smtp.d/config.h diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 6ee93c18..5e8f0650 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -52,7 +52,8 @@ EXTRA_DIST = t \ check_swap.d \ check_dbi.d \ check_ssh.d \ - check_dns.d + check_dns.d \ + check_smtp.d PLUGINHDRS = common.h diff --git a/plugins/check_smtp.c b/plugins/check_smtp.c index bb45e01b..0be02937 100644 --- a/plugins/check_smtp.c +++ b/plugins/check_smtp.c @@ -28,6 +28,7 @@ * *****************************************************************************/ +#include "states.h" const char *progname = "check_smtp"; const char *copyright = "2000-2024"; const char *email = "devel@monitoring-plugins.org"; @@ -36,21 +37,12 @@ const char *email = "devel@monitoring-plugins.org"; #include "netutils.h" #include "utils.h" #include "base64.h" +#include "regex.h" #include - -#ifdef HAVE_SSL -static bool check_cert = false; -static int days_till_exp_warn, days_till_exp_crit; -# define my_recv(buf, len) (((use_starttls || use_ssl) && ssl_established) ? np_net_ssl_read(buf, len) : read(sd, buf, len)) -# define my_send(buf, len) (((use_starttls || use_ssl) && ssl_established) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0)) -#else /* ifndef HAVE_SSL */ -# define my_recv(buf, len) read(sd, buf, len) -# define my_send(buf, len) send(sd, buf, len, 0) -#endif +#include "check_smtp.d/config.h" #define PROXY_PREFIX "PROXY TCP4 0.0.0.0 0.0.0.0 25 25\r\n" -#define SMTP_EXPECT "220" #define SMTP_HELO "HELO " #define SMTP_EHLO "EHLO " #define SMTP_LHLO "LHLO " @@ -60,71 +52,51 @@ static int days_till_exp_warn, days_till_exp_crit; #define EHLO_SUPPORTS_STARTTLS 1 -static int process_arguments (int, char **); -static int validate_arguments (void); -static void print_help (void); -void print_usage (void); -static void smtp_quit(void); -static int recvline(char *, size_t); -static int recvlines(char *, size_t); -static int my_close(void); +typedef struct { + int errorcode; + check_smtp_config config; +} check_smtp_config_wrapper; +static check_smtp_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); + +int my_recv(check_smtp_config config, void *buf, int num, int socket_descriptor, bool ssl_established) { +#ifdef HAVE_SSL + if ((config.use_starttls || config.use_ssl) && ssl_established) { + return np_net_ssl_read(buf, num); + } + return (int)read(socket_descriptor, buf, (size_t)num); +#else /* ifndef HAVE_SSL */ + return read(socket_descriptor, buf, len) +#endif +} + +int my_send(check_smtp_config config, void *buf, int num, int socket_descriptor, bool ssl_established) { +#ifdef HAVE_SSL + if ((config.use_starttls || config.use_ssl) && ssl_established) { + + return np_net_ssl_write(buf, num); + } + return (int)send(socket_descriptor, buf, (size_t)num, 0); +#else /* ifndef HAVE_SSL */ + return send(socket_descriptor, buf, len, 0); +#endif +} + +static void print_help(void); +void print_usage(void); +static char *smtp_quit(check_smtp_config /*config*/, char /*buffer*/[MAX_INPUT_BUFFER], int /*socket_descriptor*/, + bool /*ssl_established*/); +static int recvline(char * /*buf*/, size_t /*bufsize*/, check_smtp_config /*config*/, int /*socket_descriptor*/, bool /*ssl_established*/); +static int recvlines(check_smtp_config /*config*/, char * /*buf*/, size_t /*bufsize*/, int /*socket_descriptor*/, bool /*ssl_established*/); +static int my_close(int /*socket_descriptor*/); -#include "regex.h" -static regex_t preg; -static regmatch_t pmatch[10]; -static char errbuf[MAX_INPUT_BUFFER]; -static int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE; -static int eflags = 0; -static int errcode, excode; - -static int server_port_option = 0; -static char *server_address = NULL; -static char *server_expect = NULL; -static char *mail_command = NULL; -static char *from_arg = NULL; -static int send_mail_from = 0; -static int ncommands = 0; -static int command_size = 0; -static int nresponses = 0; -static int response_size = 0; -static char **commands = NULL; -static char **responses = NULL; -static char *authtype = NULL; -static char *authuser = NULL; -static char *authpass = NULL; -static double warning_time = 0; -static bool check_warning_time = false; -static double critical_time = 0; -static bool check_critical_time = false; static int verbose = 0; -static bool use_ssl = false; -static bool use_starttls = false; -static bool use_sni = false; -static bool use_proxy_prefix = false; -static bool use_ehlo = false; -static bool use_lhlo = false; -static bool ssl_established = false; -static char *localhostname = NULL; -static int sd; -static char buffer[MAX_INPUT_BUFFER]; + enum { TCP_PROTOCOL = 1, UDP_PROTOCOL = 2, }; -static bool ignore_send_quit_failure = false; int main(int argc, char **argv) { - bool supports_tls = false; - int n = 0; - double elapsed_time; - long microsec; - int result = STATE_UNKNOWN; - char *cmd_str = NULL; - char *helocmd = NULL; - char *error_msg = ""; - char *server_response = NULL; - struct timeval tv; - /* Catch pipe errors in read/write - sometimes occurs when writing QUIT */ (void)signal(SIGPIPE, SIG_IGN); @@ -135,25 +107,31 @@ int main(int argc, char **argv) { /* Parse extra opts if any */ argv = np_extra_opts(&argc, argv, progname); - if (process_arguments(argc, argv) == ERROR) { + check_smtp_config_wrapper tmp_config = process_arguments(argc, argv); + + if (tmp_config.errorcode == ERROR) { usage4(_("Could not parse arguments")); } + const check_smtp_config config = tmp_config.config; + /* If localhostname not set on command line, use gethostname to set */ + char *localhostname = config.localhostname; if (!localhostname) { localhostname = malloc(HOST_MAX_BYTES); if (!localhostname) { printf(_("malloc() failed!\n")); - return STATE_CRITICAL; + exit(STATE_CRITICAL); } if (gethostname(localhostname, HOST_MAX_BYTES)) { printf(_("gethostname() failed!\n")); - return STATE_CRITICAL; + exit(STATE_CRITICAL); } } - if (use_lhlo) { + char *helocmd = NULL; + if (config.use_lhlo) { xasprintf(&helocmd, "%s%s%s", SMTP_LHLO, localhostname, "\r\n"); - } else if (use_ehlo) { + } else if (config.use_ehlo) { xasprintf(&helocmd, "%s%s%s", SMTP_EHLO, localhostname, "\r\n"); } else { xasprintf(&helocmd, "%s%s%s", SMTP_HELO, localhostname, "\r\n"); @@ -163,10 +141,13 @@ int main(int argc, char **argv) { printf("HELOCMD: %s", helocmd); } + char *mail_command = strdup("MAIL "); + + char *cmd_str = NULL; /* initialize the MAIL command with optional FROM command */ - xasprintf(&cmd_str, "%sFROM:<%s>%s", mail_command, from_arg, "\r\n"); + xasprintf(&cmd_str, "%sFROM:<%s>%s", mail_command, config.from_arg, "\r\n"); - if (verbose && send_mail_from) { + if (verbose && config.send_mail_from) { printf("FROM CMD: %s", cmd_str); } @@ -176,84 +157,90 @@ int main(int argc, char **argv) { /* set socket timeout */ (void)alarm(socket_timeout); + struct timeval start_time; /* start timer */ - gettimeofday(&tv, NULL); + gettimeofday(&start_time, NULL); + int socket_descriptor = 0; /* try to connect to the host at the given port number */ - result = my_tcp_connect(server_address, server_port, &sd); + mp_state_enum result = my_tcp_connect(config.server_address, config.server_port, &socket_descriptor); + char *error_msg = ""; + char buffer[MAX_INPUT_BUFFER]; + bool ssl_established = false; if (result == STATE_OK) { /* we connected */ /* If requested, send PROXY header */ - if (use_proxy_prefix) { + if (config.use_proxy_prefix) { if (verbose) { printf("Sending header %s\n", PROXY_PREFIX); } - my_send(PROXY_PREFIX, strlen(PROXY_PREFIX)); + my_send(config, PROXY_PREFIX, strlen(PROXY_PREFIX), socket_descriptor, ssl_established); } #ifdef HAVE_SSL - if (use_ssl) { - result = np_net_ssl_init_with_hostname(sd, (use_sni ? server_address : NULL)); + 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(sd); + close(socket_descriptor); np_net_ssl_cleanup(); - return STATE_CRITICAL; - } else { - ssl_established = 1; + exit(STATE_CRITICAL); } + ssl_established = true; } #endif /* watch for the SMTP connection string and */ /* return a WARNING status if we couldn't read any data */ - if (recvlines(buffer, MAX_INPUT_BUFFER) <= 0) { + if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) { printf(_("recv() failed\n")); - return STATE_WARNING; + exit(STATE_WARNING); } + char *server_response = NULL; /* save connect return (220 hostname ..) for later use */ xasprintf(&server_response, "%s", buffer); /* send the HELO/EHLO command */ - my_send(helocmd, strlen(helocmd)); + my_send(config, helocmd, (int)strlen(helocmd), socket_descriptor, ssl_established); /* allow for response to helo command to reach us */ - if (recvlines(buffer, MAX_INPUT_BUFFER) <= 0) { + if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) { printf(_("recv() failed\n")); - return STATE_WARNING; - } else if (use_ehlo || use_lhlo) { + exit(STATE_WARNING); + } + 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 (use_starttls && !supports_tls) { + if (config.use_starttls && !supports_tls) { printf(_("WARNING - TLS not supported by server\n")); - smtp_quit(); - return STATE_WARNING; + smtp_quit(config, buffer, socket_descriptor, ssl_established); + exit(STATE_WARNING); } #ifdef HAVE_SSL - if (use_starttls) { + if (config.use_starttls) { /* send the STARTTLS command */ - send(sd, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0); + send(socket_descriptor, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0); - recvlines(buffer, MAX_INPUT_BUFFER); /* wait for it */ + 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(); - return STATE_UNKNOWN; + smtp_quit(config, buffer, socket_descriptor, ssl_established); + exit(STATE_UNKNOWN); } - result = np_net_ssl_init_with_hostname(sd, (use_sni ? server_address : NULL)); + 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(sd); + close(socket_descriptor); np_net_ssl_cleanup(); - return STATE_CRITICAL; - } else { - ssl_established = 1; + exit(STATE_CRITICAL); } + ssl_established = true; /* * Resend the EHLO command. @@ -266,29 +253,32 @@ int main(int argc, char **argv) { * reason, some MTAs will not allow an AUTH LOGIN command before * we resent EHLO via TLS. */ - if (my_send(helocmd, strlen(helocmd)) <= 0) { + 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(); - return STATE_UNKNOWN; + my_close(socket_descriptor); + exit(STATE_UNKNOWN); } + if (verbose) { printf(_("sent %s"), helocmd); } - if ((n = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { + + 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(); - return STATE_UNKNOWN; + my_close(socket_descriptor); + exit(STATE_UNKNOWN); } + if (verbose) { printf("%s", buffer); } # ifdef USE_OPENSSL - if (check_cert) { - result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit); - smtp_quit(); - my_close(); - return result; + 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 */ } @@ -304,76 +294,81 @@ int main(int argc, char **argv) { strip(server_response); /* make sure we find the droids we are looking for */ - if (!strstr(server_response, server_expect)) { - if (server_port == SMTP_PORT) { + 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"), server_port, server_response); + printf(_("Invalid SMTP response received from host on port %d: %s\n"), config.server_port, server_response); } - return STATE_WARNING; + exit(STATE_WARNING); } - if (send_mail_from) { - my_send(cmd_str, strlen(cmd_str)); - if (recvlines(buffer, MAX_INPUT_BUFFER) >= 1 && verbose) { + 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); } } - n = 0; - while (n < ncommands) { - xasprintf(&cmd_str, "%s%s", commands[n], "\r\n"); - my_send(cmd_str, strlen(cmd_str)); - if (recvlines(buffer, MAX_INPUT_BUFFER) >= 1 && verbose) { + 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 (n < nresponses) { - cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; - errcode = regcomp(&preg, responses[n], cflags); + 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")); - return ERROR; + exit(STATE_UNKNOWN); } - excode = regexec(&preg, buffer, 10, pmatch, eflags); + + 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, commands[n]); + 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; } } - n++; + counter++; } - if (authtype != NULL) { - if (strcmp(authtype, "LOGIN") == 0) { + if (config.authtype != NULL) { + if (strcmp(config.authtype, "LOGIN") == 0) { char *abuf; int ret; do { - if (authuser == NULL) { + if (config.authuser == NULL) { result = STATE_CRITICAL; xasprintf(&error_msg, _("no authuser specified, ")); break; } - if (authpass == NULL) { + if (config.authpass == NULL) { result = STATE_CRITICAL; xasprintf(&error_msg, _("no authpass specified, ")); break; } /* send AUTH LOGIN */ - my_send(SMTP_AUTH_LOGIN, strlen(SMTP_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(buffer, MAX_INPUT_BUFFER)) <= 0) { + 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; @@ -389,14 +384,14 @@ int main(int argc, char **argv) { } /* encode authuser with base64 */ - base64_encode_alloc(authuser, strlen(authuser), &abuf); + base64_encode_alloc(config.authuser, strlen(config.authuser), &abuf); xasprintf(&abuf, "%s\r\n", abuf); - my_send(abuf, strlen(abuf)); + my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established); if (verbose) { printf(_("sent %s\n"), abuf); } - if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { + 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; @@ -410,13 +405,13 @@ int main(int argc, char **argv) { break; } /* encode authpass with base64 */ - base64_encode_alloc(authpass, strlen(authpass), &abuf); + base64_encode_alloc(config.authpass, strlen(config.authpass), &abuf); xasprintf(&abuf, "%s\r\n", abuf); - my_send(abuf, strlen(abuf)); + my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established); if (verbose) { printf(_("sent %s\n"), abuf); } - if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { + 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; @@ -438,41 +433,36 @@ int main(int argc, char **argv) { } /* tell the server we're done */ - smtp_quit(); + smtp_quit(config, buffer, socket_descriptor, ssl_established); /* finally close the connection */ - close(sd); + close(socket_descriptor); } /* reset the alarm */ alarm(0); - microsec = deltime(tv); - elapsed_time = (double)microsec / 1.0e6; + long microsec = deltime(start_time); + double elapsed_time = (double)microsec / 1.0e6; if (result == STATE_OK) { - if (check_critical_time && elapsed_time > critical_time) { + if (config.check_critical_time && elapsed_time > config.critical_time) { result = STATE_CRITICAL; - } else if (check_warning_time && elapsed_time > warning_time) { + } else if (config.check_warning_time && elapsed_time > config.warning_time) { result = STATE_WARNING; } } 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", (int)check_warning_time, warning_time, (int)check_critical_time, critical_time, true, 0, - false, 0)); + fperfdata("time", elapsed_time, "s", config.check_warning_time, config.warning_time, config.check_critical_time, + config.critical_time, true, 0, false, 0)); - return result; + exit(result); } /* process command-line arguments */ -int process_arguments(int argc, char **argv) { - int c; - char *temp; - - bool implicit_tls = false; - +check_smtp_config_wrapper process_arguments(int argc, char **argv) { enum { SNI_OPTION }; @@ -506,109 +496,118 @@ int process_arguments(int argc, char **argv) { {"proxy", no_argument, 0, 'r'}, {0, 0, 0, 0}}; + check_smtp_config_wrapper result = { + .config = check_smtp_config_init(), + .errorcode = OK, + }; + if (argc < 2) { - return ERROR; + result.errorcode = ERROR; + return result; } - for (c = 1; c < argc; c++) { - if (strcmp("-to", argv[c]) == 0) { - strcpy(argv[c], "-t"); - } else if (strcmp("-wt", argv[c]) == 0) { - strcpy(argv[c], "-w"); - } else if (strcmp("-ct", argv[c]) == 0) { - strcpy(argv[c], "-c"); + for (int index = 1; index < argc; index++) { + if (strcmp("-to", argv[index]) == 0) { + strcpy(argv[index], "-t"); + } else if (strcmp("-wt", argv[index]) == 0) { + strcpy(argv[index], "-w"); + } else if (strcmp("-ct", argv[index]) == 0) { + strcpy(argv[index], "-c"); } } - while (1) { - c = getopt_long(argc, argv, "+hVv46Lrt:p:f:e:c:w:H:C:R:sSD:F:A:U:P:q", longopts, &option); + int command_size = 0; + int response_size = 0; + bool implicit_tls = false; + while (true) { + int opt_index = getopt_long(argc, argv, "+hVv46Lrt:p:f:e:c:w:H:C:R:sSD:F:A:U:P:q", longopts, &option); - if (c == -1 || c == EOF) { + if (opt_index == -1 || opt_index == EOF) { break; } - switch (c) { + switch (opt_index) { case 'H': /* hostname */ if (is_host(optarg)) { - server_address = optarg; + result.config.server_address = optarg; } else { usage2(_("Invalid hostname/address"), optarg); } break; case 'p': /* port */ if (is_intpos(optarg)) { - server_port_option = atoi(optarg); + result.config.server_port = atoi(optarg); } else { usage4(_("Port must be a positive integer")); } break; case 'F': /* localhostname */ - localhostname = strdup(optarg); + result.config.localhostname = strdup(optarg); break; case 'f': /* from argument */ - from_arg = optarg + strspn(optarg, "<"); - from_arg = strndup(from_arg, strcspn(from_arg, ">")); - send_mail_from = 1; + result.config.from_arg = optarg + strspn(optarg, "<"); + result.config.from_arg = strndup(result.config.from_arg, strcspn(result.config.from_arg, ">")); + result.config.send_mail_from = true; break; case 'A': - authtype = optarg; - use_ehlo = true; + result.config.authtype = optarg; + result.config.use_ehlo = true; break; case 'U': - authuser = optarg; + result.config.authuser = optarg; break; case 'P': - authpass = optarg; + result.config.authpass = optarg; break; case 'e': /* server expect string on 220 */ - server_expect = optarg; + result.config.server_expect = optarg; break; case 'C': /* commands */ - if (ncommands >= command_size) { + if (result.config.ncommands >= command_size) { command_size += 8; - commands = realloc(commands, sizeof(char *) * command_size); - if (commands == NULL) { - die(STATE_UNKNOWN, _("Could not realloc() units [%d]\n"), ncommands); + 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"), result.config.ncommands); } } - commands[ncommands] = (char *)malloc(sizeof(char) * 255); - strncpy(commands[ncommands], optarg, 255); - ncommands++; + result.config.commands[result.config.ncommands] = (char *)malloc(sizeof(char) * 255); + strncpy(result.config.commands[result.config.ncommands], optarg, 255); + result.config.ncommands++; break; case 'R': /* server responses */ - if (nresponses >= response_size) { + if (result.config.nresponses >= response_size) { response_size += 8; - responses = realloc(responses, sizeof(char *) * response_size); - if (responses == NULL) { - die(STATE_UNKNOWN, _("Could not realloc() units [%d]\n"), nresponses); + 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"), result.config.nresponses); } } - responses[nresponses] = (char *)malloc(sizeof(char) * 255); - strncpy(responses[nresponses], optarg, 255); - nresponses++; + result.config.responses[result.config.nresponses] = (char *)malloc(sizeof(char) * 255); + 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 { - critical_time = strtod(optarg, NULL); - check_critical_time = true; + result.config.critical_time = strtod(optarg, NULL); + result.config.check_critical_time = true; } break; case 'w': /* warning time threshold */ if (!is_nonnegative(optarg)) { usage4(_("Warning time must be a positive")); } else { - warning_time = strtod(optarg, NULL); - check_warning_time = true; + result.config.warning_time = strtod(optarg, NULL); + result.config.check_warning_time = true; } break; case 'v': /* verbose */ verbose++; break; case 'q': - ignore_send_quit_failure = true; /* ignore problem sending QUIT */ + result.config.ignore_send_quit_failure = true; /* ignore problem sending QUIT */ break; case 't': /* timeout */ if (is_intnonneg(optarg)) { @@ -617,30 +616,31 @@ int process_arguments(int argc, char **argv) { usage4(_("Timeout interval must be a positive integer")); } break; - case 'D': + case 'D': { /* Check SSL cert validity */ #ifdef USE_OPENSSL + char *temp; if ((temp = strchr(optarg, ',')) != NULL) { *temp = '\0'; if (!is_intnonneg(optarg)) { usage2("Invalid certificate expiration period", optarg); } - days_till_exp_warn = atoi(optarg); + result.config.days_till_exp_warn = atoi(optarg); *temp = ','; temp++; if (!is_intnonneg(temp)) { usage2(_("Invalid certificate expiration period"), temp); } - days_till_exp_crit = atoi(temp); + result.config.days_till_exp_crit = atoi(temp); } else { - days_till_exp_crit = 0; + result.config.days_till_exp_crit = 0; if (!is_intnonneg(optarg)) { usage2("Invalid certificate expiration period", optarg); } - days_till_exp_warn = atoi(optarg); + result.config.days_till_exp_warn = atoi(optarg); } - check_cert = true; - ignore_send_quit_failure = true; + result.config.check_cert = true; + result.config.ignore_send_quit_failure = true; #else usage(_("SSL support not available - install OpenSSL and recompile")); #endif @@ -648,26 +648,27 @@ int process_arguments(int argc, char **argv) { // fallthrough case 's': /* ssl */ - use_ssl = true; - server_port = SMTPS_PORT; + result.config.use_ssl = true; + result.config.server_port = SMTPS_PORT; break; case 'S': /* starttls */ - use_starttls = true; - use_ehlo = true; + result.config.use_starttls = true; + result.config.use_ehlo = true; break; + } case SNI_OPTION: #ifdef HAVE_SSL - use_sni = true; + result.config.use_sni = true; #else usage(_("SSL support not available - install OpenSSL and recompile")); #endif break; case 'r': - use_proxy_prefix = true; + result.config.use_proxy_prefix = true; break; case 'L': - use_lhlo = true; + result.config.use_lhlo = true; break; case '4': address_family = AF_INET; @@ -690,60 +691,36 @@ int process_arguments(int argc, char **argv) { } } - c = optind; - if (server_address == NULL) { + int c = optind; + if (result.config.server_address == NULL) { if (argv[c]) { if (is_host(argv[c])) { - server_address = argv[c]; + result.config.server_address = argv[c]; } else { usage2(_("Invalid hostname/address"), argv[c]); } - } else { - xasprintf(&server_address, "127.0.0.1"); } } - if (server_expect == NULL) { - server_expect = strdup(SMTP_EXPECT); - } - - if (mail_command == NULL) { - mail_command = strdup("MAIL "); - } - - if (from_arg == NULL) { - from_arg = strdup(" "); - } - - if (use_starttls && use_ssl) { + if (result.config.use_starttls && result.config.use_ssl) { if (implicit_tls) { - use_ssl = false; - server_port = SMTP_PORT; + result.config.use_ssl = false; } else { usage4(_("Set either -s/--ssl/--tls or -S/--starttls")); } } - if (server_port_option != 0) { - server_port = server_port_option; - } - - return validate_arguments(); + return result; } -int validate_arguments(void) { return OK; } - -void smtp_quit(void) { - int bytes; - int n; - - n = my_send(SMTP_QUIT, strlen(SMTP_QUIT)); - if (n < 0) { - if (ignore_send_quit_failure) { +char *smtp_quit(check_smtp_config config, char buffer[MAX_INPUT_BUFFER], int socket_descriptor, bool ssl_established) { + int sent_bytes = my_send(config, SMTP_QUIT, strlen(SMTP_QUIT), socket_descriptor, ssl_established); + if (sent_bytes < 0) { + if (config.ignore_send_quit_failure) { if (verbose) { printf(_("Connection closed by server before sending QUIT command\n")); } - return; + return buffer; } die(STATE_UNKNOWN, _("Connection closed by server before sending QUIT command\n")); } @@ -753,7 +730,7 @@ void smtp_quit(void) { } /* read the response but don't care about problems */ - bytes = recvlines(buffer, MAX_INPUT_BUFFER); + int bytes = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established); if (verbose) { if (bytes < 0) { printf(_("recv() failed after QUIT.")); @@ -764,6 +741,8 @@ void smtp_quit(void) { printf(_("received %s\n"), buffer); } } + + return buffer; } /* @@ -775,20 +754,20 @@ void smtp_quit(void) { * function which buffers the data, move that to netutils.c and change * check_smtp and other plugins to use that. Also, remove (\r)\n. */ -int recvline(char *buf, size_t bufsize) { +int recvline(char *buf, size_t bufsize, check_smtp_config config, int socket_descriptor, bool ssl_established) { int result; - unsigned i; + int counter; - for (i = result = 0; i < bufsize - 1; i++) { - if ((result = my_recv(&buf[i], 1)) != 1) { + for (counter = result = 0; counter < bufsize - 1; counter++) { + if ((result = my_recv(config, &buf[counter], 1, socket_descriptor, ssl_established)) != 1) { break; } - if (buf[i] == '\n') { - buf[++i] = '\0'; - return i; + if (buf[counter] == '\n') { + buf[++counter] = '\0'; + return counter; } } - return (result == 1 || i == 0) ? -2 : result; /* -2 if out of space */ + return (result == 1 || counter == 0) ? -2 : result; /* -2 if out of space */ } /* @@ -805,22 +784,23 @@ int recvline(char *buf, size_t bufsize) { * * TODO: Move this to netutils.c. Also, remove \r and possibly the final \n. */ -int recvlines(char *buf, size_t bufsize) { - int result, i; +int recvlines(check_smtp_config config, char *buf, size_t bufsize, int socket_descriptor, bool ssl_established) { + int result; + int counter; - for (i = 0; /* forever */; i += result) { - if (!((result = recvline(buf + i, bufsize - i)) > 3 && isdigit((int)buf[i]) && isdigit((int)buf[i + 1]) && - isdigit((int)buf[i + 2]) && buf[i + 3] == '-')) { + for (counter = 0; /* forever */; counter += result) { + if (!((result = recvline(buf + counter, bufsize - counter, config, socket_descriptor, ssl_established)) > 3 && + isdigit((int)buf[counter]) && isdigit((int)buf[counter + 1]) && isdigit((int)buf[counter + 2]) && buf[counter + 3] == '-')) { break; } } - return (result <= 0) ? result : result + i; + return (result <= 0) ? result : result + counter; } -int my_close(void) { +int my_close(int socket_descriptor) { int result; - result = close(sd); + result = close(socket_descriptor); #ifdef HAVE_SSL np_net_ssl_cleanup(); #endif diff --git a/plugins/check_smtp.d/config.h b/plugins/check_smtp.d/config.h new file mode 100644 index 00000000..c8660445 --- /dev/null +++ b/plugins/check_smtp.d/config.h @@ -0,0 +1,92 @@ +#pragma once + +#include "../../config.h" +#include +#include + +enum { + SMTP_PORT = 25, + SMTPS_PORT = 465 +}; + +#define SMTP_EXPECT "220" + +typedef struct { + int server_port; + char *server_address; + char *localhostname; + char *server_expect; + bool ignore_send_quit_failure; + + double warning_time; + bool check_warning_time; + double critical_time; + bool check_critical_time; + bool use_ehlo; + bool use_lhlo; + + char *from_arg; + bool send_mail_from; + + int ncommands; + char **commands; + + int nresponses; + char **responses; + + char *authtype; + char *authuser; + char *authpass; + + bool use_proxy_prefix; +#ifdef HAVE_SSL + bool check_cert; + int days_till_exp_warn; + int days_till_exp_crit; + bool use_ssl; + bool use_starttls; + bool use_sni; +#endif +} check_smtp_config; + +check_smtp_config check_smtp_config_init() { + check_smtp_config tmp = { + .server_port = SMTP_PORT, + .server_address = strdup("localhost"), + .localhostname = NULL, + + .server_expect = SMTP_EXPECT, + .ignore_send_quit_failure = false, + + .warning_time = 0, + .check_warning_time = false, + .critical_time = 0, + .check_critical_time = false, + .use_ehlo = false, + .use_lhlo = false, + + .from_arg = strdup(" "), + .send_mail_from = false, + + .ncommands = 0, + .commands = NULL, + + .nresponses = 0, + .responses = NULL, + + .authtype = NULL, + .authuser = NULL, + .authpass = NULL, + + .use_proxy_prefix = false, +#ifdef HAVE_SSL + .check_cert = false, + .days_till_exp_warn = 0, + .days_till_exp_crit = 0, + .use_ssl = false, + .use_starttls = false, + .use_sni = false, +#endif + }; + return tmp; +} -- cgit v1.2.3-74-g34f1 From acf57dba5233926a95fc746429f31f3009111b6f Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 10 Mar 2025 01:32:49 +0100 Subject: check_smtp: Remove unused enum --- plugins/check_smtp.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/plugins/check_smtp.c b/plugins/check_smtp.c index 0be02937..1b36a22a 100644 --- a/plugins/check_smtp.c +++ b/plugins/check_smtp.c @@ -91,11 +91,6 @@ static int my_close(int /*socket_descriptor*/); static int verbose = 0; -enum { - TCP_PROTOCOL = 1, - UDP_PROTOCOL = 2, -}; - int main(int argc, char **argv) { /* Catch pipe errors in read/write - sometimes occurs when writing QUIT */ (void)signal(SIGPIPE, SIG_IGN); -- cgit v1.2.3-74-g34f1 From c5f873aa7b74c26c985a81bc4ffeb8d5fd1ce8a9 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 10 Mar 2025 09:56:05 +0100 Subject: check_smtp: small style fixes --- plugins/check_smtp.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/plugins/check_smtp.c b/plugins/check_smtp.c index 1b36a22a..ac0aa45d 100644 --- a/plugins/check_smtp.c +++ b/plugins/check_smtp.c @@ -28,7 +28,6 @@ * *****************************************************************************/ -#include "states.h" const char *progname = "check_smtp"; const char *copyright = "2000-2024"; const char *email = "devel@monitoring-plugins.org"; @@ -41,6 +40,7 @@ const char *email = "devel@monitoring-plugins.org"; #include #include "check_smtp.d/config.h" +#include "../lib/states.h" #define PROXY_PREFIX "PROXY TCP4 0.0.0.0 0.0.0.0 25 25\r\n" #define SMTP_HELO "HELO " @@ -92,9 +92,6 @@ static int my_close(int /*socket_descriptor*/); static int verbose = 0; int main(int argc, char **argv) { - /* Catch pipe errors in read/write - sometimes occurs when writing QUIT */ - (void)signal(SIGPIPE, SIG_IGN); - setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); @@ -123,6 +120,7 @@ int main(int argc, char **argv) { exit(STATE_CRITICAL); } } + char *helocmd = NULL; if (config.use_lhlo) { xasprintf(&helocmd, "%s%s%s", SMTP_LHLO, localhostname, "\r\n"); @@ -137,7 +135,6 @@ int main(int argc, char **argv) { } char *mail_command = strdup("MAIL "); - char *cmd_str = NULL; /* initialize the MAIL command with optional FROM command */ xasprintf(&cmd_str, "%sFROM:<%s>%s", mail_command, config.from_arg, "\r\n"); @@ -146,6 +143,9 @@ int main(int argc, char **argv) { printf("FROM CMD: %s", cmd_str); } + /* Catch pipe errors in read/write - sometimes occurs when writing QUIT */ + (void)signal(SIGPIPE, SIG_IGN); + /* initialize alarm signal handling */ (void)signal(SIGALRM, socket_timeout_alarm_handler); @@ -204,6 +204,7 @@ int main(int argc, char **argv) { printf(_("recv() failed\n")); exit(STATE_WARNING); } + bool supports_tls = false; if (config.use_ehlo || config.use_lhlo) { if (strstr(buffer, "250 STARTTLS") != NULL || strstr(buffer, "250-STARTTLS") != NULL) { @@ -228,6 +229,7 @@ int main(int argc, char **argv) { smtp_quit(config, buffer, socket_descriptor, ssl_established); exit(STATE_UNKNOWN); } + 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")); @@ -235,6 +237,7 @@ int main(int argc, char **argv) { np_net_ssl_cleanup(); exit(STATE_CRITICAL); } + ssl_established = true; /* -- cgit v1.2.3-74-g34f1 From 897fef937096c6aae32f9cd80d7b867558596bc5 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 10 Mar 2025 20:54:52 +0100 Subject: check_smtp: small improvements --- plugins/check_smtp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/check_smtp.c b/plugins/check_smtp.c index ac0aa45d..324549d4 100644 --- a/plugins/check_smtp.c +++ b/plugins/check_smtp.c @@ -423,7 +423,7 @@ int main(int argc, char **argv) { break; } break; - } while (0); + } while (false); } else { result = STATE_CRITICAL; xasprintf(&error_msg, _("only authtype LOGIN is supported, ")); @@ -462,7 +462,7 @@ int main(int argc, char **argv) { /* process command-line arguments */ check_smtp_config_wrapper process_arguments(int argc, char **argv) { enum { - SNI_OPTION + SNI_OPTION = CHAR_MAX + 1 }; int option = 0; -- cgit v1.2.3-74-g34f1 From 5cf53de34fa410702ca59437cdf3f304366c3c55 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 10 Mar 2025 20:56:09 +0100 Subject: check_smtp: fix positional host logic --- plugins/check_smtp.c | 2 ++ plugins/check_smtp.d/config.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/check_smtp.c b/plugins/check_smtp.c index 324549d4..fd492294 100644 --- a/plugins/check_smtp.c +++ b/plugins/check_smtp.c @@ -697,6 +697,8 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) { } else { usage2(_("Invalid hostname/address"), argv[c]); } + } else { + result.config.server_address = strdup("localhost"); } } diff --git a/plugins/check_smtp.d/config.h b/plugins/check_smtp.d/config.h index c8660445..0a6511ef 100644 --- a/plugins/check_smtp.d/config.h +++ b/plugins/check_smtp.d/config.h @@ -52,7 +52,7 @@ typedef struct { check_smtp_config check_smtp_config_init() { check_smtp_config tmp = { .server_port = SMTP_PORT, - .server_address = strdup("localhost"), + .server_address = NULL, .localhostname = NULL, .server_expect = SMTP_EXPECT, -- cgit v1.2.3-74-g34f1 From 6cd097921f0c5016fcae60b38dfb88c412e4bb20 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 10 Mar 2025 21:27:50 +0100 Subject: Allow setting of port independent of argument position again --- plugins/check_smtp.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/check_smtp.c b/plugins/check_smtp.c index fd492294..44b735f9 100644 --- a/plugins/check_smtp.c +++ b/plugins/check_smtp.c @@ -517,6 +517,7 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) { int command_size = 0; int response_size = 0; bool implicit_tls = false; + int server_port_option = 0; while (true) { int opt_index = getopt_long(argc, argv, "+hVv46Lrt:p:f:e:c:w:H:C:R:sSD:F:A:U:P:q", longopts, &option); @@ -534,7 +535,7 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) { break; case 'p': /* port */ if (is_intpos(optarg)) { - result.config.server_port = atoi(optarg); + server_port_option = atoi(optarg); } else { usage4(_("Port must be a positive integer")); } @@ -710,6 +711,10 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) { } } + if (server_port_option != 0) { + result.config.server_port = server_port_option; + } + return result; } -- cgit v1.2.3-74-g34f1