SF.net SVN: nagiosplug: [1780] nagiosplug/trunk

[email protected]
Newsgroups gmane.network.nagios.plugins.cvs
Message-ID <[email protected]>
Revision: 1780
          http://nagiosplug.svn.sourceforge.net/nagiosplug/?rev=1780&view=rev
Author:   hweiss
Date:     2007-09-15 07:25:56 -0700 (Sat, 15 Sep 2007)

Log Message:
-----------
Properly handle SMTP server responses which are split into multiple
packets (noted by Chris Adams on nagiosplug-help@).  TODO: The new
recvline()/recvlines() functions should buffer received data instead of
reading one byte at a time and they should be moved to netutils.c so
that other plugins can use them, too.

Modified Paths:
--------------
    nagiosplug/trunk/NEWS
    nagiosplug/trunk/plugins/check_smtp.c

Modified: nagiosplug/trunk/NEWS
===================================================================
--- nagiosplug/trunk/NEWS	2007-09-15 11:55:12 UTC (rev 1779)
+++ nagiosplug/trunk/NEWS	2007-09-15 14:25:56 UTC (rev 1780)
@@ -15,6 +15,7 @@
 	  address to use in the DHCP request
 	The check_dhcp -r and -s options now accept host names, too
 	Fix possible check_icmp bus errors on some (non-x86/AMD64) platforms
+	Fix check_smtp's handling of multiple-packet server responses
 
 1.4.9 4th June 2006
 	Inclusion of contrib/check_cluster2 as check_cluster with some improvements

Modified: nagiosplug/trunk/plugins/check_smtp.c
===================================================================
--- nagiosplug/trunk/plugins/check_smtp.c	2007-09-15 11:55:12 UTC (rev 1779)
+++ nagiosplug/trunk/plugins/check_smtp.c	2007-09-15 14:25:56 UTC (rev 1780)
@@ -40,6 +40,8 @@
 const char *copyright = "2000-2006";
 const char *email = "[email protected]";
 
+#include <ctype.h>
+
 #include "common.h"
 #include "netutils.h"
 #include "utils.h"
@@ -75,6 +77,8 @@
 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"
@@ -116,7 +120,6 @@
 enum {
   TCP_PROTOCOL = 1,
   UDP_PROTOCOL = 2,
-  MAXBUF = 1024
 };
 
 /* written by lauri alanko */
@@ -222,7 +225,7 @@
 
 		/* 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;
 		}
@@ -246,11 +249,10 @@
 		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;
@@ -268,7 +270,7 @@
 		  /* 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"));
 		    smtp_quit();
@@ -302,13 +304,12 @@
 		}
 		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);
 		}
 
@@ -337,16 +338,14 @@
 		 */
 		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) {
@@ -395,12 +394,11 @@
 					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);
 
@@ -417,12 +415,11 @@
 					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);
 					}
@@ -438,12 +435,11 @@
 					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);
 					}
@@ -715,7 +711,7 @@
 		printf(_("sent %s\n"), "QUIT");
 
 	/* read the response but don't care about problems */
-	bytes = my_recv(buffer, MAXBUF - 1);
+	bytes = recvlines(buffer, MAX_INPUT_BUFFER);
 	if (verbose) {
 		if (bytes < 0)
 			printf(_("recv() failed after QUIT."));
@@ -729,6 +725,64 @@
 }
 
 
+/*
+ * 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)
 {


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2005.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.