# HG changeset patch # User Ben Schmidt # Date 1424848957 -39600 # Node ID 04d916168efba6736758a9e53fbefef58cc4a7b5 # Parent b0b8ccdb490f309435cb0599d53b6a301d119834 Support ESMTP so OpenSMTPD uses 8 bits (Paul Fariello). diff -r b0b8ccdb490f -r 04d916168efb ChangeLog --- a/ChangeLog Mon Mar 24 11:57:08 2014 +1100 +++ b/ChangeLog Wed Feb 25 18:22:37 2015 +1100 @@ -1,3 +1,4 @@ + o Support ESMTP so OpenSMTPD uses 8 bits (Paul Fariello) o Use iconv to convert unknown character sets o Handle unfolded header lines better o Add a tunable for moderation request lifetime (Timo Boettcher) diff -r b0b8ccdb490f -r 04d916168efb include/checkwait_smtpreply.h --- a/include/checkwait_smtpreply.h Mon Mar 24 11:57:08 2014 +1100 +++ b/include/checkwait_smtpreply.h Wed Feb 25 18:22:37 2015 +1100 @@ -25,13 +25,14 @@ #define CHECK_REPLY_H #define MLMMJ_CONNECT 1 -#define MLMMJ_HELO 2 +#define MLMMJ_EHLO 2 +#define MLMMJ_HELO 3 #define MLMMJ_FROM 4 -#define MLMMJ_RCPTTO 8 -#define MLMMJ_DATA 16 -#define MLMMJ_DOT 32 -#define MLMMJ_QUIT 64 -#define MLMMJ_RSET 128 +#define MLMMJ_RCPTTO 5 +#define MLMMJ_DATA 6 +#define MLMMJ_DOT 7 +#define MLMMJ_QUIT 8 +#define MLMMJ_RSET 9 #include "mlmmj.h" diff -r b0b8ccdb490f -r 04d916168efb include/mail-functions.h --- a/include/mail-functions.h Mon Mar 24 11:57:08 2014 +1100 +++ b/include/mail-functions.h Wed Feb 25 18:22:37 2015 +1100 @@ -29,6 +29,7 @@ #include int write_helo(int sockfd, const char *hostname); +int write_ehlo(int sockfd, const char *hostname); int write_mail_from(int sockfd, const char *from_addr, const char *extra); int write_rcpt_to(int sockfd, const char *rcpt_addr); int write_custom_line(int sockfd, const char *line); diff -r b0b8ccdb490f -r 04d916168efb src/checkwait_smtpreply.c --- a/src/checkwait_smtpreply.c Mon Mar 24 11:57:08 2014 +1100 +++ b/src/checkwait_smtpreply.c Wed Feb 25 18:22:37 2015 +1100 @@ -33,9 +33,18 @@ char *checkwait_smtpreply(int sockfd, int replytype) { - char *smtpreply; + char *smtpreply = NULL; + if(replytype == MLMMJ_EHLO) { + /* Consume all 8BITMIME 250- reply */ + do { + myfree(smtpreply); smtpreply = mygetline(sockfd); + } while (strncmp(smtpreply, "250-", 4) == 0); + } else { + smtpreply = mygetline(sockfd); + } + if(smtpreply == NULL) { /* This will never be a valid SMTP response so will always be returned, * but is more descriptive than an empty string. */ @@ -55,6 +64,10 @@ if(smtpreply[0] != '2' || smtpreply[1] != '2') return smtpreply; break; + case MLMMJ_EHLO: + if(smtpreply[0] != '2' || smtpreply[1] != '5') + return smtpreply; + break; case MLMMJ_HELO: if(smtpreply[0] != '2' || smtpreply[1] != '5') return smtpreply; diff -r b0b8ccdb490f -r 04d916168efb src/mail-functions.c --- a/src/mail-functions.c Mon Mar 24 11:57:08 2014 +1100 +++ b/src/mail-functions.c Wed Feb 25 18:22:37 2015 +1100 @@ -37,11 +37,35 @@ #include "log_error.h" #include "memory.h" +/* "EHLO \r\n" has length 7 */ +#define EXTRA_EHLO_LEN 7 +int write_ehlo(int sockfd, const char *hostname) +{ + size_t len = (size_t)(strlen(hostname) + EXTRA_EHLO_LEN + 1); + char *ehlo; + size_t bytes_written; + + if((ehlo = mymalloc(len)) == 0) + return errno; + snprintf(ehlo, len, "EHLO %s\r\n", hostname); + len = strlen(ehlo); +#if 0 + fprintf(stderr, "\nwrite_ehlo, ehlo = [%s]\n", ehlo); +#endif + bytes_written = writen(sockfd, ehlo, len); + if(bytes_written < 0) { + log_error(LOG_ARGS, "Could not write EHLO"); + myfree(ehlo); + return errno; + } + myfree(ehlo); + return 0; +} /* "HELO \r\n " has length 7 */ -#define EXTRA_HELO_LEN 8 +#define EXTRA_HELO_LEN 7 int write_helo(int sockfd, const char *hostname) { - size_t len = (size_t)(strlen(hostname) + EXTRA_HELO_LEN); + size_t len = (size_t)(strlen(hostname) + EXTRA_HELO_LEN + 1); char *helo; size_t bytes_written; @@ -66,7 +90,7 @@ int write_mail_from(int sockfd, const char *from_addr, const char *extra) { size_t len = (size_t)(strlen(from_addr) + EXTRA_FROM_LEN + - strlen(extra) + 2); + strlen(extra) + 1); char *mail_from; size_t bytes_written; @@ -95,11 +119,10 @@ } /* "RCPT TO: <>\r\n" has length 13 */ -#define EXTRA_RCPT_LEN 14 - +#define EXTRA_RCPT_LEN 13 int write_rcpt_to(int sockfd, const char *rcpt_addr) { - size_t len = (size_t)(strlen(rcpt_addr) + EXTRA_RCPT_LEN); + size_t len = (size_t)(strlen(rcpt_addr) + EXTRA_RCPT_LEN + 1); char *rcpt_to; size_t bytes_written; @@ -241,9 +264,6 @@ return retstr; } -/* "\r\n" has length 2 */ -#define EXTRA_CUSTOM_LEN 3 - int write_dot(int sockfd) { size_t bytes_written; @@ -255,9 +275,11 @@ return 0; } +/* "\r\n" has length 2 */ +#define EXTRA_CUSTOM_LEN 2 int write_custom_line(int sockfd, const char *line) { - size_t len = strlen(line) + EXTRA_CUSTOM_LEN; + size_t len = strlen(line) + EXTRA_CUSTOM_LEN + 1; size_t bytes_written; char *customline; @@ -281,11 +303,10 @@ } /* "Reply-To: \r\n" has length 12 */ -#define EXTRA_REPLYTO_LEN 13 - +#define EXTRA_REPLYTO_LEN 12 int write_replyto(int sockfd, const char *replyaddr) { - size_t len = (size_t)(strlen(replyaddr) + EXTRA_REPLYTO_LEN); + size_t len = (size_t)(strlen(replyaddr) + EXTRA_REPLYTO_LEN + 1); char *replyto; size_t bytes_written; diff -r b0b8ccdb490f -r 04d916168efb src/mlmmj-send.c --- a/src/mlmmj-send.c Mon Mar 24 11:57:08 2014 +1100 +++ b/src/mlmmj-send.c Wed Feb 25 18:22:37 2015 +1100 @@ -379,13 +379,17 @@ int initsmtp(int *sockfd, const char *relayhost, unsigned short port) { int retval = 0; + int try_ehlo = 1; char *reply = NULL; char *myhostname = hostnamestr(); + do { init_sockfd(sockfd, relayhost, port); - if(*sockfd == -1) - return EBADF; + if(*sockfd == -1) { + retval = EBADF; + break; + } if((reply = checkwait_smtpreply(*sockfd, MLMMJ_CONNECT)) != NULL) { log_error(LOG_ARGS, "No proper greeting to our connect" @@ -393,16 +397,104 @@ myfree(reply); retval = MLMMJ_CONNECT; /* FIXME: Queue etc. */ + break; } - write_helo(*sockfd, myhostname); - myfree(myhostname); - if((reply = checkwait_smtpreply(*sockfd, MLMMJ_HELO)) != NULL) { - log_error(LOG_ARGS, "Error with HELO. Reply: [%s]", reply); - /* FIXME: quit and tell admin to configure correctly */ + + if (try_ehlo) { + write_ehlo(*sockfd, myhostname); + if((reply = checkwait_smtpreply(*sockfd, MLMMJ_EHLO)) + == NULL) { + /* EHLO successful don't try more */ + break; + } + + /* RFC 1869 - 4.5. - In the case of any error response, + * the client SMTP should issue either the HELO or QUIT + * command. + * RFC 1869 - 4.5. - If the server SMTP recognizes the + * EHLO command, but the command argument is + * unacceptable, it will return code 501. + */ + if (strncmp(reply, "501", 3) == 0) { myfree(reply); - retval = MLMMJ_HELO; + /* Commmand unacceptable; we choose to QUIT but + * ignore any QUIT errors; return that EHLO was + * the error. + */ + endsmtp(sockfd); + retval = MLMMJ_EHLO; + break; + } + + /* RFC 1869 - 4.6. - A server SMTP that conforms to RFC + * 821 but does not support the extensions specified + * here will not recognize the EHLO command and will + * consequently return code 500, as specified in RFC + * 821. The server SMTP should stay in the same state + * after returning this code (see section 4.1.1 of RFC + * 821). The client SMTP may then issue either a HELO + * or a QUIT command. + */ + + if (reply[0] != '5') { + myfree(reply); + /* Server doesn't understand EHLO, but gives a + * broken response. Try with new connection. + */ + endsmtp(sockfd); + try_ehlo = 0; + continue; } + myfree(reply); + + /* RFC 1869 - 4.7. - Other improperly-implemented + * servers will not accept a HELO command after EHLO has + * been sent and rejected. In some cases, this problem + * can be worked around by sending a RSET after the + * failure response to EHLO, then sending the HELO. + */ + write_rset(*sockfd); + reply = checkwait_smtpreply(*sockfd, MLMMJ_RSET); + + /* RFC 1869 - 4.7. - Clients that do this should be + * aware that many implementations will return a failure + * code (e.g., 503 Bad sequence of commands) in response + * to the RSET. This code can be safely ignored. + */ + myfree(reply); + + /* Try HELO on the same connection + */ + } + + write_helo(*sockfd, myhostname); + if((reply = checkwait_smtpreply(*sockfd, MLMMJ_HELO)) + == NULL) { + /* EHLO successful don't try more */ + break; + } + if (try_ehlo) { + myfree(reply); + /* We reused a connection we tried EHLO on. Maybe + * that's why it failed. Try with new connection. + */ + endsmtp(sockfd); + try_ehlo = 0; + continue; + } + + log_error(LOG_ARGS, "Error with HELO. Reply: " + "[%s]", reply); + myfree(reply); + /* FIXME: quit and tell admin to configure + * correctly */ + retval = MLMMJ_HELO; + break; + + } while (1); + + myfree(myhostname); return retval; }