From 23677908dda6eefe3b32ed8af16a05050c846afa Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Tue, 8 May 2007 23:50:35 +0200 Subject: [PATCH] [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. --- doc/haproxy-en.txt | 7 +++++++ doc/haproxy-fr.txt | 8 ++++++++ include/common/defaults.h | 1 + include/types/backend.h | 1 + src/cfgparse.c | 32 ++++++++++++++++++++++++++++++-- src/checks.c | 7 ++++++- 6 files changed, 53 insertions(+), 3 deletions(-) diff --git a/doc/haproxy-en.txt b/doc/haproxy-en.txt index 76d54c463..1341aea53 100644 --- a/doc/haproxy-en.txt +++ b/doc/haproxy-en.txt @@ -1034,6 +1034,13 @@ the response is considered as valid. Note that Apache does not generate a log 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 8e83eab09..795b67b3c 100644 --- a/doc/haproxy-fr.txt +++ b/doc/haproxy-fr.txt @@ -1044,6 +1044,14 @@ r 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 c99aafe4f..cfe60e8f9 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 e2d4efc6f..57a8cb053 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 41d706611..b2e76b310 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -1029,13 +1029,14 @@ int cfg_parse_listen(const char *file, int linenum, char **args) 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 @@ int cfg_parse_listen(const char *file, int linenum, char **args) 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 14f0c8cc8..ec56453a5 100644 --- a/src/checks.c +++ b/src/checks.c @@ -122,7 +122,8 @@ static int event_srv_chk_w(int fd) 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 @@ static int event_srv_chk_r(int fd) /* 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;