[MEDIUM] implement SMTP health checks

Peter van Dijk contributed this patch which implements the "smtpchk"
option, which is to SMTP what "httpchk" is to HTTP. By default, it sends
"HELO localhost" to the servers, and waits for the 250 message, but it
can also send a specific request.
diff --git a/doc/haproxy-en.txt b/doc/haproxy-en.txt
index 76d54c4..1341aea 100644
--- a/doc/haproxy-en.txt
+++ b/doc/haproxy-en.txt
@@ -1034,6 +1034,13 @@
 when it receives only an HELLO message, which makes this type of message
 perfectly suit this need.
 
+Version 1.3.10 introduced the SMTP health check. By default, it sends
+"HELO localhost" to the servers, and waits for the 250 message. Note that it
+can also send a specific request :
+
+  - option smtpchk                         -> sends "HELO localhost"
+  - option smtpchk EHLO mail.mydomain.com  -> sends this ESMTP greeting
+
 See examples below.
 
 Since version 1.1.17, it is possible to specify backup servers. These servers
diff --git a/doc/haproxy-fr.txt b/doc/haproxy-fr.txt
index 8e83eab..795b67b 100644
--- a/doc/haproxy-fr.txt
+++ b/doc/haproxy-fr.txt
@@ -1044,6 +1044,14 @@
 lorsqu'il reçoit des messages HELLO, ce qui en fait un type de message
 parfaitement adapté à ce besoin.
 
+La version 1.3.10 est accompagnée d'un nouveau test d'état pour le SMTP. Par
+défaut, il consiste à envoyer "HELO localhost" aux serveurs, et à attendre le
+message "250" en retour. Notez qu'il peut aussi envoyer une requête plus
+spécifique :
+
+  - option smtpchk                         -> envoie "HELO localhost"
+  - option smtpchk EHLO mail.mydomain.com  -> envoie ce message ESMTP
+
 Voir les exemples ci-après.        
 
 Depuis la version 1.1.17, il est possible de définir des serveurs de secours,
diff --git a/include/common/defaults.h b/include/common/defaults.h
index c99aafe..cfe60e8 100644
--- a/include/common/defaults.h
+++ b/include/common/defaults.h
@@ -92,6 +92,7 @@
 #define DEF_FALLTIME    3
 #define DEF_RISETIME    2
 #define DEF_CHECK_REQ   "OPTIONS / HTTP/1.0\r\n\r\n"
+#define DEF_SMTP_CHECK_REQ   "HELO localhost\r\n"
 
 /* Default connections limit.
  *
diff --git a/include/types/backend.h b/include/types/backend.h
index e2d4efc..57a8cb0 100644
--- a/include/types/backend.h
+++ b/include/types/backend.h
@@ -59,6 +59,7 @@
 #define	PR_O_TCPSPLICE	0x08000000      /* delegate data transfer to linux kernel's tcp_splice */
 #define PR_O_BALANCE_UH 0x10000000      /* balance on URI hash */
 #define PR_O_BALANCE    (PR_O_BALANCE_RR | PR_O_BALANCE_SH | PR_O_BALANCE_UH)
+#define PR_O_SMTP_CHK   0x20000000      /* use SMTP EHLO check for server health - pvandijk@vision6.com.au */
 
 
 #endif /* _TYPES_BACKEND_H */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 41d7066..b2e76b3 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -1029,13 +1029,14 @@
 			if (curproxy->check_req != NULL) {
 				free(curproxy->check_req);
 			}
-			curproxy->options |= PR_O_HTTP_CHK;
 			curproxy->options &= ~PR_O_SSL3_CHK;
+			curproxy->options &= ~PR_O_SMTP_CHK;
+			curproxy->options |= PR_O_HTTP_CHK;
 			if (!*args[2]) { /* no argument */
 				curproxy->check_req = strdup(DEF_CHECK_REQ); /* default request */
 				curproxy->check_len = strlen(DEF_CHECK_REQ);
 			} else if (!*args[3]) { /* one argument : URI */
-				int reqlen = strlen(args[2]) + strlen("OPTIONS / HTTP/1.0\r\n\r\n");
+				int reqlen = strlen(args[2]) + strlen("OPTIONS  HTTP/1.0\r\n\r\n") + 1;
 				curproxy->check_req = (char *)malloc(reqlen);
 				curproxy->check_len = snprintf(curproxy->check_req, reqlen,
 							       "OPTIONS %s HTTP/1.0\r\n\r\n", args[2]); /* URI to use */
@@ -1060,8 +1061,35 @@
 				free(curproxy->check_req);
 			}
 			curproxy->options &= ~PR_O_HTTP_CHK;
+			curproxy->options &= ~PR_O_SMTP_CHK;
 			curproxy->options |= PR_O_SSL3_CHK;
 		}
+		else if (!strcmp(args[1], "smtpchk")) {
+			/* use SMTP request to check servers' health */
+			if (curproxy->check_req != NULL) {
+				free(curproxy->check_req);
+			}
+			curproxy->options &= ~PR_O_HTTP_CHK;
+			curproxy->options &= ~PR_O_SSL3_CHK;
+			curproxy->options |= PR_O_SMTP_CHK;
+
+			if (!*args[2] || !*args[3]) { /* no argument or incomplete EHLO host */
+				curproxy->check_req = strdup(DEF_SMTP_CHECK_REQ); /* default request */
+				curproxy->check_len = strlen(DEF_SMTP_CHECK_REQ);
+			} else { /* ESMTP EHLO, or SMTP HELO, and a hostname */
+				if (!strcmp(args[2], "EHLO") || !strcmp(args[2], "HELO")) {
+					int reqlen = strlen(args[2]) + strlen(args[3]) + strlen(" \r\n") + 1;
+					curproxy->check_req = (char *)malloc(reqlen);
+					curproxy->check_len = snprintf(curproxy->check_req, reqlen,
+								       "%s %s\r\n", args[2], args[3]); /* HELO hostname */
+				} else {
+					/* this just hits the default for now, but you could potentially expand it to allow for other stuff
+					   though, it's unlikely you'd want to send anything other than an EHLO or HELO */
+					curproxy->check_req = strdup(DEF_SMTP_CHECK_REQ); /* default request */
+					curproxy->check_len = strlen(DEF_SMTP_CHECK_REQ);
+				}
+			}
+		}
 		else if (!strcmp(args[1], "forwardfor")) {
 			/* insert x-forwarded-for field, but not for the
 			 * IP address listed as an except.
diff --git a/src/checks.c b/src/checks.c
index 14f0c8c..ec56453 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -122,7 +122,8 @@
 	if (s->result != -1) {
 		/* we don't want to mark 'UP' a server on which we detected an error earlier */
 		if ((s->proxy->options & PR_O_HTTP_CHK) ||
-		    (s->proxy->options & PR_O_SSL3_CHK)) {
+		    (s->proxy->options & PR_O_SSL3_CHK) ||
+		    (s->proxy->options & PR_O_SMTP_CHK)) {
 			int ret;
 			/* we want to check if this host replies to HTTP or SSLv3 requests
 			 * so we'll send the request, and won't wake the checker up now.
@@ -252,6 +253,10 @@
 		/* SSLv3 alert or handshake */
 		result = 1;
 	}
+	else if ((s->proxy->options & PR_O_SMTP_CHK) && (len >= 3) &&
+		   (reply[0] == '2')) /* 2xx (should be 250) */ {
+		result = 1;
+	}
 
 	if (result == -1)
 		fdtab[fd].state = FD_STERROR;