diff --git a/plugins/check_smtp.c b/plugins/check_smtp.c
index a7a07838f9b10f5d08063467045b5f27f0aeaf25..8ff10b82a9d9f2a6ea4f9e36115e472b719b87d7 100644 (file)
--- a/plugins/check_smtp.c
+++ b/plugins/check_smtp.c
-/******************************************************************************
-*
+/*****************************************************************************
+*
* Nagios check_smtp plugin
-*
+*
* License: GPL
-* Copyright (c) 1999-2006 nagios-plugins team
-*
+* Copyright (c) 2000-2007 Nagios Plugins Development Team
+*
* Last Modified: $Date$
-*
+*
* Description:
-*
+*
* This file contains the check_smtp plugin
-*
-* This plugin will attempt to open an SMTP connection with the host.
-*
-*
-* License Information:
-*
-* This program is free software; you can redistribute it and/or modify
+*
+* 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 2 of the License, or
+* 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, write to the Free Software
-* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*
-*
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*
* $Id$
*
-******************************************************************************/
+*****************************************************************************/
const char *progname = "check_smtp";
const char *revision = "$Revision$";
-const char *copyright = "2000-2006";
+const char *copyright = "2000-2007";
const char *email = "nagiosplug-devel@lists.sourceforge.net";
#include "common.h"
#include "netutils.h"
#include "utils.h"
+#include "base64.h"
+
+#include <ctype.h>
#ifdef HAVE_SSL
int check_cert = FALSE;
int validate_arguments (void);
void print_help (void);
void print_usage (void);
+void smtp_quit(void);
+int recvline(char *, size_t);
+int recvlines(char *, size_t);
int my_close(void);
#include "regex.h"
enum {
TCP_PROTOCOL = 1,
UDP_PROTOCOL = 2,
- MAXBUF = 1024
};
-/* written by lauri alanko */
-static char *
-base64 (const char *bin, size_t len)
-{
-
- char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
- size_t i = 0, j = 0;
-
- char BASE64_END = '=';
- char base64_table[64];
- strncpy (base64_table, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 64);
-
- while (j < len - 2) {
- buf[i++] = base64_table[bin[j] >> 2];
- buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
- buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
- buf[i++] = base64_table[bin[j + 2] & 63];
- j += 3;
- }
-
- switch (len - j) {
- case 1:
- buf[i++] = base64_table[bin[j] >> 2];
- buf[i++] = base64_table[(bin[j] & 3) << 4];
- buf[i++] = BASE64_END;
- buf[i++] = BASE64_END;
- break;
- case 2:
- buf[i++] = base64_table[bin[j] >> 2];
- buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
- buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
- buf[i++] = BASE64_END;
- break;
- case 0:
- break;
- }
-
- buf[i] = '\0';
- return buf;
-}
int
main (int argc, char **argv)
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
+ /* Parse extra opts if any */
+ argv=np_extra_opts (&argc, argv, progname);
+
if (process_arguments (argc, argv) == ERROR)
usage4 (_("Could not parse arguments"));
/* watch for the SMTP connection string and */
/* return a WARNING status if we couldn't read any data */
- if (recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0) == -1) {
+ if (recvlines(buffer, MAX_INPUT_BUFFER) <= 0) {
printf (_("recv() failed\n"));
result = STATE_WARNING;
}
/* make sure we find the response we are looking for */
if (!strstr (buffer, server_expect)) {
if (server_port == SMTP_PORT)
- printf (_("Invalid SMTP response received from host\n"));
+ printf (_("Invalid SMTP response received from host: %s\n"), buffer);
else
- printf (_("Invalid SMTP response received from host on port %d\n"),
- server_port);
+ printf (_("Invalid SMTP response received from host on port %d: %s\n"),
+ server_port, buffer);
result = STATE_WARNING;
}
}
send(sd, helocmd, strlen(helocmd), 0);
/* allow for response to helo command to reach us */
- if(read (sd, buffer, MAXBUF - 1) < 0){
+ if (recvlines(buffer, MAX_INPUT_BUFFER) <= 0) {
printf (_("recv() failed\n"));
return STATE_WARNING;
} else if(use_ehlo){
- buffer[MAXBUF-1]='\0';
if(strstr(buffer, "250 STARTTLS") != NULL ||
strstr(buffer, "250-STARTTLS") != NULL){
supports_tls=TRUE;
if(use_ssl && ! supports_tls){
printf(_("WARNING - TLS not supported by server\n"));
- send (sd, SMTP_QUIT, strlen (SMTP_QUIT), 0);
+ smtp_quit();
return STATE_WARNING;
}
/* send the STARTTLS command */
send(sd, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0);
- recv(sd,buffer, MAX_INPUT_BUFFER-1, 0); /* wait for it */
+ recvlines(buffer, MAX_INPUT_BUFFER); /* wait for it */
if (!strstr (buffer, server_expect)) {
printf (_("Server does not support STARTTLS\n"));
- send (sd, SMTP_QUIT, strlen (SMTP_QUIT), 0);
+ smtp_quit();
return STATE_UNKNOWN;
}
result = np_net_ssl_init(sd);
}
if (verbose)
printf(_("sent %s"), helocmd);
- if ((n = my_recv(buffer, MAX_INPUT_BUFFER - 1)) <= 0) {
+ 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) {
- buffer[n] = '\0';
printf("%s", buffer);
}
*/
if (smtp_use_dummycmd) {
my_send(cmd_str, strlen(cmd_str));
- my_recv(buffer, MAX_INPUT_BUFFER-1);
- if (verbose)
+ if (recvlines(buffer, MAX_INPUT_BUFFER) >= 1 && verbose)
printf("%s", buffer);
}
while (n < ncommands) {
asprintf (&cmd_str, "%s%s", commands[n], "\r\n");
my_send(cmd_str, strlen(cmd_str));
- my_recv(buffer, MAX_INPUT_BUFFER-1);
- if (verbose)
+ if (recvlines(buffer, MAX_INPUT_BUFFER) >= 1 && verbose)
printf("%s", buffer);
strip (buffer);
if (n < nresponses) {
if (verbose)
printf (_("sent %s\n"), "AUTH LOGIN");
- if((ret = my_recv(buffer, MAXBUF - 1)) < 0){
+ if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) {
asprintf(&error_msg, _("recv() failed after AUTH LOGIN, "));
result = STATE_WARNING;
break;
}
- buffer[ret] = 0;
if (verbose)
printf (_("received %s\n"), buffer);
}
/* encode authuser with base64 */
- abuf = base64 (authuser, strlen(authuser));
+ base64_encode_alloc (authuser, strlen(authuser), &abuf);
+ /* FIXME: abuf shouldn't have enough space to strcat a '\r\n' into it. */
strcat (abuf, "\r\n");
my_send(abuf, strlen(abuf));
if (verbose)
printf (_("sent %s\n"), abuf);
- if ((ret = my_recv(buffer, MAX_INPUT_BUFFER-1)) == -1) {
+ if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) {
result = STATE_CRITICAL;
asprintf(&error_msg, _("recv() failed after sending authuser, "));
break;
}
- buffer[ret] = 0;
if (verbose) {
printf (_("received %s\n"), buffer);
}
break;
}
/* encode authpass with base64 */
- abuf = base64 (authpass, strlen(authpass));
+ base64_encode_alloc (authpass, strlen(authpass), &abuf);
+ /* FIXME: abuf shouldn't have enough space to strcat a '\r\n' into it. */
strcat (abuf, "\r\n");
my_send(abuf, strlen(abuf));
if (verbose) {
printf (_("sent %s\n"), abuf);
}
- if ((ret = my_recv(buffer, MAX_INPUT_BUFFER-1)) == -1) {
+ if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) {
result = STATE_CRITICAL;
asprintf(&error_msg, _("recv() failed after sending authpass, "));
break;
}
- buffer[ret] = 0;
if (verbose) {
printf (_("received %s\n"), buffer);
}
}
/* tell the server we're done */
- my_send (SMTP_QUIT, strlen (SMTP_QUIT));
+ smtp_quit();
/* finally close the connection */
close (sd);
break;
case 'A':
authtype = optarg;
+ use_ehlo = TRUE;
break;
case 'U':
authuser = optarg;
}
+void
+smtp_quit(void)
+{
+ int bytes;
+
+ my_send(SMTP_QUIT, strlen(SMTP_QUIT));
+ 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)
+ printf(_("recv() failed after QUIT."));
+ else if (bytes == 0)
+ printf(_("Connection reset by peer."));
+ 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
+ * error.
+ *
+ * TODO: Reading one byte at a time is very inefficient. Replace this by a
+ * 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 result;
+ unsigned i;
+
+ for (i = result = 0; i < bufsize - 1; i++) {
+ 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 */
+}
+
+
+/*
+ * 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
+ * error. Works for all protocols which format multiline replies as follows:
+ *
+ * ``The format for multiline replies requires that every line, except the last,
+ * begin with the reply code, followed immediately by a hyphen, `-' (also known
+ * as minus), followed by text. The last line will begin with the reply code,
+ * followed immediately by <SP>, optionally some text, and <CRLF>. As noted
+ * above, servers SHOULD send the <SP> if subsequent text is not sent, but
+ * clients MUST be prepared for it to be omitted.'' (RFC 2821, 4.2.1)
+ *
+ * TODO: Move this to netutils.c. Also, remove \r and possibly the final \n.
+ */
+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] == '-'))
+ break;
+
+ return (result <= 0) ? result : result + i;
+}
+
+
int
my_close (void)
{
print_usage ();
printf (_(UT_HELP_VRSN));
+ printf (_(UT_EXTRA_OPTS));
printf (_(UT_HOST_PORT), 'p', myport);
printf ("%s\n", _("connects, but incorrect reponse messages from the host result in"));
printf ("%s\n", _("STATE_WARNING return values."));
+#ifdef NP_EXTRA_OPTS
+ printf ("\n");
+ printf ("%s\n", _("Notes:"));
+ printf (_(UT_EXTRA_OPTS_NOTES));
+#endif
+
printf (_(UT_SUPPORT));
}