From 859afd6c154625e18be4b2f8123e40cd9e0cce05 Mon Sep 17 00:00:00 2001 From: manuel Date: Mon, 19 Jan 2026 22:40:44 +0100 Subject: qmail-remote: show EHLO response even after connection has been dropped this was actually way harder than assumed. turns out we need to issue HELO after EHLO, in case EHLO is not supported. however qmail overwrites the old reply as soon as we issue the next command. furthermore some mail servers drop the connection after issueing a 5xx reply. we only notice this after reading from the socket which usually happens on the next smtp commmand. so we need to run HELO to determine if the connection has been dropped, however running HELO truncates the EHLO reply we want to show in the bounce message. and we have TLS-required as possible variants. so after EHLO fails.. * in TLS-required: show EHLO reponse in case EHLO code is non-success * in non-TLS: copy EHLO response, issue HELO, show EHLO response in case connection dies, otherwise show HELO response --- qmail-remote.c | 52 +++++++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/qmail-remote.c b/qmail-remote.c index ee2c6a8..dbeef95 100644 --- a/qmail-remote.c +++ b/qmail-remote.c @@ -138,6 +138,20 @@ void outhost() } int flagcritical = 0; +stralloc smtptext = {0}; +stralloc smtptextdropped = {0}; + +void outsmtptext(stralloc *smtptext) +{ + int i; + if (smtptext->s && smtptext->len) { + out("Remote host said: "); + for (i = 0;i < smtptext->len;++i) + if (!smtptext->s[i]) smtptext->s[i] = '?'; + if (substdio_put(subfdoutsmall,smtptext->s,smtptext->len) == -1) _exit(0); + smtptext->len = 0; + } +} void dropped() { out("ZConnected to "); @@ -148,6 +162,7 @@ void dropped() { if (ssl_err_str) { out(ssl_err_str); out(" "); } #endif out("(#4.4.2)\n"); + outsmtptext(&smtptextdropped); zerodie(); } @@ -176,7 +191,7 @@ ssize_t safewrite(fd,buf,len) int fd; char *buf; int len; r = ssl_timeoutwrite(timeout, smtpfd, smtpfd, ssl, buf, len); if (r < 0) ssl_err_str = ssl_error_str(); } else -#endif +#endif r = timeoutwrite(timeout,smtpfd,buf,len); if (r <= 0) dropped(); return r; @@ -189,8 +204,6 @@ substdio smtpto = SUBSTDIO_FDBUF(safewrite,-1,smtptobuf,sizeof smtptobuf); char smtpfrombuf[128]; substdio smtpfrom = SUBSTDIO_FDBUF(saferead,-1,smtpfrombuf,sizeof smtpfrombuf); -stralloc smtptext = {0}; - void get(ch) char *ch; { @@ -276,18 +289,6 @@ unsigned long ehlo() return 250; } -void outsmtptext() -{ - int i; - if (smtptext.s) if (smtptext.len) { - out("Remote host said: "); - for (i = 0;i < smtptext.len;++i) - if (!smtptext.s[i]) smtptext.s[i] = '?'; - if (substdio_put(subfdoutsmall,smtptext.s,smtptext.len) == -1) _exit(0); - smtptext.len = 0; - } -} - void smtp_quit() { #ifdef TLS @@ -306,7 +307,7 @@ void quit2(const char *prepend, const char *append, int flagdie) outhost(); if (append) out(append); out(".\n"); - outsmtptext(); + outsmtptext(&smtptext); #if defined(TLS) && defined(DEBUG) if (ssl) { @@ -394,7 +395,7 @@ int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; } * @return 0 ...TLS failed, continue plaintext * @return -1 ...skip to next MX */ -static int tls_init(struct ip_mx *current_mx) +static int tls_init(unsigned long code, struct ip_mx *current_mx) { int i; SSL *myssl; @@ -476,7 +477,9 @@ static int tls_init(struct ip_mx *current_mx) if (!tls_required) return 0; out(failure_class); out("TLS is required, but was not offered by host"); - smtptext.len = 0; + /* only hide SMTP EHLO reply in bounce message on success */ + if (code == 250) + smtptext.len = 0; TLS_QUIT; } } @@ -634,7 +637,7 @@ static int tls_init(struct ip_mx *current_mx) /* read the responce to STARTTLS */ if (!smtps) { - unsigned int code = smtpcode(); + unsigned long code = smtpcode(); if (code != 220) { SSL_free(myssl); if (!tls_required) return 0; @@ -731,7 +734,7 @@ static int tls_init(struct ip_mx *current_mx) } if (smtps) { - unsigned int code = smtpcode(); + unsigned long code = smtpcode(); if (code >= 500 && code < 600) quit("DTLS connected to "," but greeting failed"); if (code >= 400 && code < 500) return -1; /* try next MX, see RFC-2821 */ if (code != 220) quit("ZTLS connected to "," but greeting failed"); @@ -823,7 +826,7 @@ static void smtp(struct ip_mx *current_mx) #endif #ifdef TLS - int tls = tls_init(current_mx); + int tls = tls_init(code, current_mx); if (tls == -1) { if (ssl) ssl_free(ssl); @@ -842,11 +845,14 @@ static void smtp(struct ip_mx *current_mx) /* and if EHLO failed, use HELO */ } else { + /* copy EHLO reply for bounce message in case the connection has already been dropped */ + if (!stralloc_copy(&smtptextdropped,&smtptext)) temp_nomem(); substdio_puts(&smtpto,"HELO "); substdio_put(&smtpto,helohost.s,helohost.len); substdio_puts(&smtpto,"\r\n"); substdio_flush(&smtpto); code = smtpcode(); + if (!stralloc_copys(&smtptextdropped,"")) temp_nomem(); if (code >= 500) quit("DConnected to "," but my name was rejected"); if (code != 250) quit("ZConnected to "," but my name was rejected"); } @@ -911,12 +917,12 @@ static void smtp(struct ip_mx *current_mx) if (code >= 500) { out("h"); out(auth_status.s); outhost(); out(" does not like recipient.\n"); - outsmtptext(); zero(); + outsmtptext(&smtptext); zero(); } else if (code >= 400) { out("s"); out(auth_status.s); outhost(); out(" does not like recipient.\n"); - outsmtptext(); zero(); + outsmtptext(&smtptext); zero(); } else { /* -- cgit v1.2.3