BUG/MINOR: smtpchk: SMTP Service check should gracefully close SMTP transaction

At present option smtpchk closes the TCP connection abruptly on completion of service checking,
even if successful. This can result in a very high volume of errors in backend SMTP server logs.
This patch ensures an SMTP QUIT is sent and a positive 2xx response is received from the SMTP
server prior to disconnection.

This patch depends on the following one:

 * MINOR: smtpchk: Update expect rule to fully match replies to EHLO commands

This patch should fix the issue #1812. It may be backported as far as 2.2
with the commit above On the 2.2, proxy_parse_smtpchk_opt() function is
located in src/check.c

[cf: I updated reg-tests script accordingly]

(cherry picked from commit 9a8d8a3fd0828a1cb64745318dcc5704a0b4b1a9)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 3593db1a396bd87785871f6682d5080a739d7728)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 8cedc5b32a5d025e1da54e91238a000d1937f749)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/src/tcpcheck.c b/src/tcpcheck.c
index 9459493..6bfb601 100644
--- a/src/tcpcheck.c
+++ b/src/tcpcheck.c
@@ -6,6 +6,7 @@
  * Copyright 2013 Baptiste Assmann <bedis9@gmail.com>
  * Copyright 2020 Gaetan Rivet <grive@u256.net>
  * Copyright 2020 Christopher Faulet <cfaulet@haproxy.com>
+ * Crown Copyright 2022 Defence Science and Technology Laboratory <dstlipgroup@dstl.gov.uk>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -4357,6 +4358,32 @@
 	chk->index = 4;
 	LIST_APPEND(&rs->rules, &chk->list);
 
+        /* Send an SMTP QUIT to ensure clean disconnect (issue 1812), and expect a 2xx response code */
+
+        chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", "QUIT\r\n", ""},
+                                  1, curpx, &rs->rules, file, line, &errmsg);
+        if (!chk) {
+                ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+                goto error;
+        }
+        chk->index = 5;
+        LIST_APPEND(&rs->rules, &chk->list);
+
+        chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
+                                               "min-recv", "4",
+                                               "error-status", "L7STS",
+                                               "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
+                                               "on-success", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
+                                               "status-code", "res.payload(0,3)",
+                                               ""},
+                                    1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
+        if (!chk) {
+                ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+                goto error;
+        }
+        chk->index = 6;
+        LIST_APPEND(&rs->rules, &chk->list);
+
   ruleset_found:
 	rules->list = &rs->rules;
 	rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);