]> git.ipfire.org Git - thirdparty/mlmmj.git/commitdiff
Support ESMTP so OpenSMTPD uses 8 bits (Paul Fariello).
authorBen Schmidt <none@none>
Wed, 25 Feb 2015 07:22:37 +0000 (18:22 +1100)
committerBen Schmidt <none@none>
Wed, 25 Feb 2015 07:22:37 +0000 (18:22 +1100)
--HG--
extra : histedit_source : 8cbbdb1e9294b967618df89f7396ef370c3dea87

ChangeLog
include/checkwait_smtpreply.h
include/mail-functions.h
src/checkwait_smtpreply.c
src/mail-functions.c
src/mlmmj-send.c

index d8ca81a029bc183c50725947eae0b37ef5234940..4503e08103a949bace3ec3bd44e481239d43111e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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)
index 8d031a20c13f532d38a9126178b1e93b7cd64dd0..42fc12da1bab31126f99e713fddabe2ab972ea0c 100644 (file)
 #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"
 
index 488a80c77d64d1665a2f9d5bdaa00afb5621f21b..2c17c77eec0df7ee084570b735692a1011a3c265 100644 (file)
@@ -29,6 +29,7 @@
 #include <stdio.h>
 
 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);
index fac99f244a2e795e3e918c28749a0b356347442b..c09b4fbe7a49de1d5b45b28c60ec1a85518a6562 100644 (file)
 
 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);
+       }
 
-       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 @@ char *checkwait_smtpreply(int sockfd, int replytype)
                        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;
index 724d2ffdbea2adca7b122f016704f9149f8d4c73..373cd844d7dd1bd8b1f6e3bf5828260d3cc5f414 100644 (file)
 #include "log_error.h"
 #include "memory.h"
 
-/* "HELO \r\n " has length 7 */
-#define EXTRA_HELO_LEN 8
+/* "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 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_helo(int sockfd, const char *hostname)
 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 @@ int write_mail_from(int sockfd, const char *from_addr, const char *extra)
 }
 
 /* "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 @@ char *get_prepped_mailbody_from_map(char *mapstart, size_t size, size_t *blen)
        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 @@ int write_dot(int sockfd)
        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 @@ int write_custom_line(int sockfd, const char *line)
 }
 
 /* "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;
        
index 5ba1acc3b837b0c1008d2251267e961e8705d262..17e8efed33f281c96e7257222775ee8be47a60fe 100644 (file)
@@ -379,30 +379,122 @@ int send_mail(int sockfd, const char *from, const char *to,
 int initsmtp(int *sockfd, const char *relayhost, unsigned short port)
 {
        int retval = 0;
+       int try_ehlo = 1;
        char *reply = NULL;
        char *myhostname = hostnamestr();
 
-       init_sockfd(sockfd, relayhost, port);
+       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"
-                         "Reply: [%s]", reply);
-               myfree(reply);
-               retval = MLMMJ_CONNECT;
-               /* FIXME: Queue etc. */
-       }       
-       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((reply = checkwait_smtpreply(*sockfd, MLMMJ_CONNECT)) != NULL) {
+                       log_error(LOG_ARGS, "No proper greeting to our connect"
+                                       "Reply: [%s]", reply);
+                       myfree(reply);
+                       retval = MLMMJ_CONNECT;
+                       /* FIXME: Queue etc. */
+                       break;
+               }
+
+               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);
+                               /* 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;
 }