Added dovecot authenticator.
authorPhilip Hazel <ph10@hermes.cam.ac.uk>
Mon, 2 Oct 2006 13:38:17 +0000 (13:38 +0000)
committerPhilip Hazel <ph10@hermes.cam.ac.uk>
Mon, 2 Oct 2006 13:38:17 +0000 (13:38 +0000)
18 files changed:
doc/doc-txt/ChangeLog
doc/doc-txt/NewStuff
src/ACKNOWLEDGMENTS
src/scripts/MakeLinks
src/src/EDITME
src/src/auths/Makefile
src/src/auths/dovecot.c [new file with mode: 0644]
src/src/auths/dovecot.h [new file with mode: 0644]
src/src/config.h.defaults
src/src/drtables.c
src/src/exim.c
test/confs/3650 [new file with mode: 0644]
test/log/3650 [new file with mode: 0644]
test/rejectlog/3650 [new file with mode: 0644]
test/scripts/3650-Dovecot/3650 [new file with mode: 0644]
test/scripts/3650-Dovecot/REQUIRES [new file with mode: 0644]
test/stderr/3650 [new file with mode: 0644]
test/stdout/3650 [new file with mode: 0644]

index 261a42c..d19ddda 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.399 2006/09/26 13:51:57 ph10 Exp $
+$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.400 2006/10/02 13:38:17 ph10 Exp $
 
 Change log file for Exim from version 4.21
 -------------------------------------------
@@ -64,6 +64,21 @@ PH/11 Callouts were setting the name used for EHLO/HELO from $smtp_active_
       there is no remote transport (possible with a router that sets up host
       addresses), $smtp_active_hostname is used.
 
+PH/12 Installed Andrey Panin's patch to add a dovecot authenticator. Various
+      tweaks were necessary in order to get it to work:
+      (a) The code assumed that strncpy() returns a negative number on buffer
+          overflow, which isn't the case. Replaced with Exim's string_format()
+          function.
+      (b) There were several signed/unsigned issues. I just did the minimum
+          hacking in of casts. There is scope for a larger refactoring.
+      (c) The code used strcasecmp() which is not a standard C function.
+          Replaced with Exim's strcmpic() function.
+      (d) The code set only $1; it now sets $auth1 as well.
+      (e) A simple test gave the error "authentication client didn't specify
+          service in request". It would seem that Dovecot has changed its
+          interface. Fortunately there's a specification; I followed it and
+          changed what the client sends and it appears to be working now.
+
 
 Exim version 4.63
 -----------------
index ae4043c..263df87 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-txt/NewStuff,v 1.111 2006/09/25 10:14:20 ph10 Exp $
+$Cambridge: exim/doc/doc-txt/NewStuff,v 1.112 2006/10/02 13:38:18 ph10 Exp $
 
 New Features in Exim
 --------------------
@@ -50,6 +50,26 @@ Version 4.64
    The modifier can be used in SMTP and non-SMTP ACLs. It applies to both
    permanent and temporary rejections.
 
+3. There is a new authenticator called "dovecot". This is an interface to the
+   authentication facility of the Dovecot POP/IMAP server, which can support a
+   number of authentication methods. If you are using Dovecot to authenticate
+   POP/IMAP clients, it might be helpful to use the same mechanisms for SMTP
+   authentication. This is a server authenticator only. The only option is
+   server_socket, which must specify the socket which is the interface to
+   Dovecot authentication. The public_name option must specify an
+   authentication mechanism that Dovecot is configured to support. You can have
+   several authenticators for different mechanisms. For example:
+
+     dovecot_plain:
+       driver = PLAIN
+       server_name = /var/run/dovecot/auth-client
+       server_setid = $auth1
+
+     dovecot_ntlm:
+       driver = NTLM
+       server_name = /var/run/dovecot/auth-client
+       server_setid = $auth1
+
 
 Version 4.63
 ------------
index 43b171e..0a92594 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/src/ACKNOWLEDGMENTS,v 1.55 2006/09/19 11:28:44 ph10 Exp $
+$Cambridge: exim/src/ACKNOWLEDGMENTS,v 1.56 2006/10/02 13:38:18 ph10 Exp $
 
 EXIM ACKNOWLEDGEMENTS
 
@@ -20,7 +20,7 @@ relatively small patches.
 Philip Hazel
 
 Lists created: 20 November 2002
-Last updated:  19 September 2006
+Last updated:  02 October 2006
 
 
 THE OLD LIST
@@ -210,6 +210,7 @@ Pete Naylor               Patch for LDAP TCP connect timeout setting
 Alexander Newmann         Diagnosing and patching obscure and subtle socket bug
 Matthew Newton            Patch for exicyclog log location problem
 Marcin Owsiany            Diagnosis of a tricky timeout failure bug
+Andrey Panin              Dovecot authenticator
 Eric Parusel              Patch for tls_remember_esmtp
 Gaige Paulsen             Amended Darwin config files
 Richard Premdas           Patch for PAM buglet
index 120a25b..d38f8b0 100755 (executable)
@@ -1,5 +1,5 @@
 #!/bin/sh
-# $Cambridge: exim/src/scripts/MakeLinks,v 1.6 2005/08/01 13:20:28 ph10 Exp $
+# $Cambridge: exim/src/scripts/MakeLinks,v 1.7 2006/10/02 13:38:18 ph10 Exp $
 
 # Script to build links for all the exim source files from the system-
 # specific build directory. It should be run from within that directory.
@@ -177,6 +177,8 @@ ln -s ../../src/auths/pwcheck.c          pwcheck.c
 ln -s ../../src/auths/pwcheck.h          pwcheck.h
 ln -s ../../src/auths/auth-spa.c         auth-spa.c
 ln -s ../../src/auths/auth-spa.h         auth-spa.h
+ln -s ../../src/auths/dovecot.c          dovecot.c
+ln -s ../../src/auths/dovecot.h          dovecot.h
 ln -s ../../src/auths/sha1.c             sha1.c
 ln -s ../../src/auths/spa.c              spa.c
 ln -s ../../src/auths/spa.h              spa.h
index 135189d..17f8329 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/src/EDITME,v 1.16 2005/10/12 15:45:38 ph10 Exp $
+# $Cambridge: exim/src/src/EDITME,v 1.17 2006/10/02 13:38:18 ph10 Exp $
 
 ##################################################
 #          The Exim mail transport agent         #
@@ -475,6 +475,7 @@ FIXED_NEVER_USERS=root
 
 # AUTH_CRAM_MD5=yes
 # AUTH_CYRUS_SASL=yes
+# AUTH_DOVECOT=yes
 # AUTH_PLAINTEXT=yes
 # AUTH_SPA=yes
 
index 0d40d45..889e24b 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/src/auths/Makefile,v 1.3 2005/09/12 13:50:03 ph10 Exp $
+# $Cambridge: exim/src/src/auths/Makefile,v 1.4 2006/10/02 13:38:18 ph10 Exp $
 
 # Make file for building a library containing all the available authorization
 # methods, and calling it auths.a. In addition, there are functions that are
@@ -9,7 +9,8 @@
 
 OBJ = b64encode.o b64decode.o call_pam.o call_pwcheck.o call_radius.o \
       xtextencode.o xtextdecode.o get_data.o get_no64_data.o md5.o \
-      cram_md5.o cyrus_sasl.o plaintext.o pwcheck.o sha1.o auth-spa.o spa.o
+      cram_md5.o cyrus_sasl.o dovecot.o plaintext.o pwcheck.o sha1.o \
+      auth-spa.o spa.o
 
 auths.a:         $(OBJ)
                 @$(RM_COMMAND) -f auths.a
@@ -38,6 +39,7 @@ xtextdecode.o:   $(HDRS) xtextdecode.c
 
 cram_md5.o:      $(HDRS) cram_md5.c cram_md5.h
 cyrus_sasl.o:    $(HDRS) cyrus_sasl.c cyrus_sasl.h
+dovecot.o:       $(HDRS) dovecot.c dovecot.h
 plaintext.o:     $(HDRS) plaintext.c plaintext.h
 spa.o:           $(HDRS) spa.c spa.h
 
diff --git a/src/src/auths/dovecot.c b/src/src/auths/dovecot.c
new file mode 100644 (file)
index 0000000..6168ac9
--- /dev/null
@@ -0,0 +1,283 @@
+/* $Cambridge: exim/src/src/auths/dovecot.c,v 1.1 2006/10/02 13:38:18 ph10 Exp $ */
+
+/*
+ * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "../exim.h"
+#include "dovecot.h"
+
+#define VERSION_MAJOR  1
+#define VERSION_MINOR  0
+
+/* Options specific to the authentication mechanism. */
+optionlist auth_dovecot_options[] = {
+       {
+               "server_socket",
+               opt_stringptr,
+               (void *)(offsetof(auth_dovecot_options_block, server_socket))
+       },
+};
+
+/* Size of the options list. An extern variable has to be used so that its
+address can appear in the tables drtables.c. */
+int auth_dovecot_options_count =
+       sizeof(auth_dovecot_options) / sizeof(optionlist);
+
+/* Default private options block for the authentication method. */
+auth_dovecot_options_block auth_dovecot_option_defaults = {
+       NULL,                           /* server_socket */
+};
+
+/*************************************************
+ *          Initialization entry point           *
+ *************************************************/
+
+/* Called for each instance, after its options have been read, to
+enable consistency checks to be done, or anything else that needs
+to be set up. */
+void auth_dovecot_init(auth_instance *ablock)
+{
+       auth_dovecot_options_block *ob =
+               (auth_dovecot_options_block *)(ablock->options_block);
+
+       if (ablock->public_name == NULL)
+               ablock->public_name = ablock->name;
+       if (ob->server_socket != NULL)
+               ablock->server = TRUE;
+       ablock->client = FALSE;
+}
+
+static int strcut(char *str, char **ptrs, int nptrs)
+{
+       char *tmp = str;
+       int n;
+
+       for (n = 0; n < nptrs; n++)
+               ptrs[n] = NULL;
+       n = 1;
+
+       while (*str) {
+               if (*str == '\t') {
+                       if (n <= nptrs) {
+                               *ptrs++ = tmp;
+                               tmp = str + 1;
+                               *str = 0;
+                       }
+                       n++;
+               }
+               str++;
+       }
+
+       if (n < nptrs)
+               *ptrs = tmp;
+
+       return n;
+}
+
+#define CHECK_COMMAND(str, arg_min, arg_max) do { \
+       if (strcasecmp((str), args[0]) != 0) \
+               goto out; \
+       if (nargs - 1 < (arg_min)) \
+               goto out; \
+       if (nargs - 1 > (arg_max)) \
+               goto out; \
+} while (0)
+
+#define OUT(msg) do { \
+       auth_defer_msg = (US msg); \
+       goto out; \
+} while(0)
+
+/*************************************************
+ *             Server entry point                *
+ *************************************************/
+
+int auth_dovecot_server(auth_instance *ablock, uschar *data)
+{
+       auth_dovecot_options_block *ob =
+               (auth_dovecot_options_block *)(ablock->options_block);
+       struct sockaddr_un sa;
+       char buffer[4096];
+       char *args[8];
+       int nargs, tmp;
+       int cuid = 0, cont = 1, found = 0, fd, ret = DEFER;
+       FILE *f;
+
+       HDEBUG(D_auth) debug_printf("dovecot authentication\n");
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sun_family = AF_UNIX;
+
+       /* This was the original code here: it is nonsense because strncpy()
+       does not return an integer. I have converted this to use the function
+       that formats and checks length. PH */
+
+       /*
+       if (strncpy(sa.sun_path, ob->server_socket, sizeof(sa.sun_path)) < 0) {
+       */
+
+       if (!string_format(US sa.sun_path, sizeof(sa.sun_path), "%s",
+                          ob->server_socket)) {
+               auth_defer_msg = US"authentication socket path too long";
+               return DEFER;
+       }
+
+       auth_defer_msg = US"authentication socket connection error";
+
+       fd = socket(PF_UNIX, SOCK_STREAM, 0);
+       if (fd < 0)
+               return DEFER;
+
+       if (connect(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0)
+               goto out;
+
+       f = fdopen(fd, "a+");
+       if (f == NULL)
+               goto out;
+
+       auth_defer_msg = US"authentication socket protocol error";
+
+       while (cont) {
+               if (fgets(buffer, sizeof(buffer), f) == NULL)
+                       OUT("authentication socket read error or premature eof");
+
+               buffer[strlen(buffer) - 1] = 0;
+               HDEBUG(D_auth) debug_printf("received: %s\n", buffer);
+               nargs = strcut(buffer, args, sizeof(args) / sizeof(args[0]));
+
+               switch (toupper(*args[0])) {
+               case 'C':
+                       CHECK_COMMAND("CUID", 1, 1);
+                       cuid = atoi(args[1]);
+                       break;
+
+               case 'D':
+                       CHECK_COMMAND("DONE", 0, 0);
+                       cont = 0;
+                       break;
+
+               case 'M':
+                       CHECK_COMMAND("MECH", 1, INT_MAX);
+                       if (strcmpic(US args[1], ablock->public_name) == 0)
+                               found = 1;
+                       break;
+
+               case 'S':
+                       CHECK_COMMAND("SPID", 1, 1);
+                       break;
+
+               case 'V':
+                       CHECK_COMMAND("VERSION", 2, 2);
+                       if (atoi(args[1]) != VERSION_MAJOR)
+                               OUT("authentication socket protocol version mismatch");
+                       break;
+
+               default:
+                       goto out;
+               }
+       }
+
+       if (!found)
+               goto out;
+
+       fprintf(f, "VERSION\t%d\t%d\nCPID\t%d\n"
+               "AUTH\t%d\t%s\tservice=smtp\trip=%s\tlip=%s\tresp=%s\n",
+               VERSION_MAJOR, VERSION_MINOR, getpid(), cuid,
+               ablock->public_name, sender_host_address, interface_address,
+               data ? (char *) data : "");
+
+/****************************************************************************
+   The code below was the original code here. It didn't work. A reading of the
+   file auth-protocol.txt.gz that came with Dovecot 1.0_beta8 indicated that
+   this was not right. Maybe something changed. I changed it to the above, and
+   it seems to be better. PH
+
+       fprintf(f, "VERSION\t%d\t%d\r\nSERVICE\tSMTP\r\nCPID\t%d\r\n"
+               "AUTH\t%d\t%s\trip=%s\tlip=%s\tresp=%s\r\n",
+               VERSION_MAJOR, VERSION_MINOR, getpid(), cuid,
+               ablock->public_name, sender_host_address, interface_address,
+               data ? (char *) data : "");
+****************************************************************************/
+
+       HDEBUG(D_auth) debug_printf("sent: VERSION\t%d\t%d\nsent: CPID\t%d\n"
+               "sent: AUTH\t%d\t%s\tservice=smtp\trip=%s\tlip=%s\tresp=%s\n",
+               VERSION_MAJOR, VERSION_MINOR, getpid(), cuid,
+               ablock->public_name, sender_host_address, interface_address,
+               data ? (char *) data : "");
+
+
+       while (1) {
+               if (fgets(buffer, sizeof(buffer), f) == NULL) {
+                       auth_defer_msg = US"authentication socket read error or premature eof";
+                       goto out;
+               }
+
+               buffer[strlen(buffer) - 1] = 0;
+               HDEBUG(D_auth) debug_printf("received: %s\n", buffer);
+               nargs = strcut(buffer, args, sizeof(args) / sizeof(args[0]));
+
+               if (atoi(args[1]) != cuid)
+                       OUT("authentication socket connection id mismatch");
+
+               switch (toupper(*args[0])) {
+               case 'C':
+                       CHECK_COMMAND("CONT", 1, 2);
+
+                       tmp = auth_get_no64_data(&data, US args[2]);
+                       if (tmp != OK) {
+                               ret = tmp;
+                               goto out;
+                       }
+
+                       if (fprintf(f, "CONT\t%d\t%s\r\n", cuid, data) < 0)
+                               OUT("authentication socket write error");
+
+                       break;
+
+               case 'F':
+                       CHECK_COMMAND("FAIL", 1, 2);
+
+                       /* FIXME: add proper response handling */
+                       if (args[2]) {
+                               uschar *p = US strchr(args[2], '=');
+                               if (p) {
+                                       ++p;
+                                       expand_nstring[1] = auth_vars[0] = p;
+                                       expand_nlength[1] = Ustrlen(p);
+                                       expand_nmax = 1;
+                               }
+                       }
+
+                       ret = FAIL;
+                       goto out;
+
+               case 'O':
+                       CHECK_COMMAND("OK", 2, 2);
+                       {
+                               /* FIXME: add proper response handling */
+                               uschar *p = US strchr(args[2], '=');
+                               if (!p)
+                                       OUT("authentication socket protocol error, username missing");
+
+                               p++;
+                               expand_nstring[1] = auth_vars[0] = p;
+                               expand_nlength[1] = Ustrlen(p);
+                               expand_nmax = 1;
+                       }
+                       ret = OK;
+                       /* fallthrough */
+
+               default:
+                       goto out;
+               }
+       }
+
+out:   close(fd);
+       return ret;
+}
diff --git a/src/src/auths/dovecot.h b/src/src/auths/dovecot.h
new file mode 100644 (file)
index 0000000..9fd324b
--- /dev/null
@@ -0,0 +1,30 @@
+/* $Cambridge: exim/src/src/auths/dovecot.h,v 1.1 2006/10/02 13:38:18 ph10 Exp $ */
+
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* Copyright (c) University of Cambridge 1995 - 2006 */
+/* See the file NOTICE for conditions of use and distribution. */
+
+/* Private structure for the private options. */
+
+typedef struct {
+  uschar *server_socket;
+} auth_dovecot_options_block;
+
+/* Data for reading the private options. */
+
+extern optionlist auth_dovecot_options[];
+extern int auth_dovecot_options_count;
+
+/* Block containing default values. */
+
+extern auth_dovecot_options_block auth_dovecot_option_defaults;
+
+/* The entry points for the mechanism */
+
+extern void auth_dovecot_init(auth_instance *);
+extern int auth_dovecot_server(auth_instance *, uschar *);
+
+/* End of dovecot.h */
index e754e39..7974a41 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/config.h.defaults,v 1.11 2006/09/19 11:28:45 ph10 Exp $ */
+/* $Cambridge: exim/src/src/config.h.defaults,v 1.12 2006/10/02 13:38:18 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -21,6 +21,7 @@ it's a default value. */
 
 #define AUTH_CRAM_MD5
 #define AUTH_CYRUS_SASL
+#define AUTH_DOVECOT
 #define AUTH_PLAINTEXT
 #define AUTH_SPA
 
index 5f3b330..e27fc56 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/drtables.c,v 1.6 2006/02/07 11:19:00 ph10 Exp $ */
+/* $Cambridge: exim/src/src/drtables.c,v 1.7 2006/10/02 13:38:18 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -551,6 +551,10 @@ set to NULL for those that are not compiled into the binary. */
 #include "auths/cyrus_sasl.h"
 #endif
 
+#ifdef AUTH_DOVECOT
+#include "auths/dovecot.h"
+#endif
+
 #ifdef AUTH_PLAINTEXT
 #include "auths/plaintext.h"
 #endif
@@ -589,6 +593,19 @@ auth_info auths_available[] = {
   },
 #endif
 
+#ifdef AUTH_DOVECOT
+  {
+  US"dovecot",                                /* lookup name */
+  auth_dovecot_options,
+  &auth_dovecot_options_count,
+  &auth_dovecot_option_defaults,
+  sizeof(auth_dovecot_options_block),
+  auth_dovecot_init,                          /* init function */
+  auth_dovecot_server,                        /* server function */
+  NULL                                        /* client function */
+  },
+#endif
+
 #ifdef AUTH_PLAINTEXT
   {
   US"plaintext",                             /* lookup name */
index d209b76..fc3e67d 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/exim.c,v 1.43 2006/09/18 14:49:23 ph10 Exp $ */
+/* $Cambridge: exim/src/src/exim.c,v 1.44 2006/10/02 13:38:18 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -980,6 +980,9 @@ fprintf(f, "Authenticators:");
 #ifdef AUTH_CYRUS_SASL
   fprintf(f, " cyrus_sasl");
 #endif
+#ifdef AUTH_DOVECOT
+  fprintf(f, " dovecot");
+#endif
 #ifdef AUTH_PLAINTEXT
   fprintf(f, " plaintext");
 #endif
diff --git a/test/confs/3650 b/test/confs/3650
new file mode 100644 (file)
index 0000000..fd75156
--- /dev/null
@@ -0,0 +1,39 @@
+# Exim test configuration 3650
+
+SERVER=
+
+exim_path = EXIM_PATH
+host_lookup_order = bydns
+primary_hostname = myhost.test.ex
+rfc1413_query_timeout = 0s
+spool_directory = DIR/spool
+log_file_path = DIR/spool/log/SERVER%slog
+gecos_pattern = ""
+gecos_name = CALLER_NAME
+
+# ----- Main settings -----
+
+acl_smtp_rcpt = check_recipient
+
+
+# ----- ACL -----
+
+begin acl
+
+check_recipient:
+  deny     message = authentication required
+          !authenticated = *
+  accept
+
+
+# ----- Authentication -----
+
+begin authenticators
+
+dovecot:
+  driver = dovecot
+  public_name = PLAIN
+  server_socket = /var/run/dovecot/auth-client
+  server_set_id = $auth1
+
+# End
diff --git a/test/log/3650 b/test/log/3650
new file mode 100644 (file)
index 0000000..8d69354
--- /dev/null
@@ -0,0 +1,4 @@
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 dovecot authenticator failed for (xxxx) [127.0.0.1]: 535 Incorrect authentication data (set_id=userx)
diff --git a/test/rejectlog/3650 b/test/rejectlog/3650
new file mode 100644 (file)
index 0000000..1af78c0
--- /dev/null
@@ -0,0 +1,3 @@
+
+******** SERVER ********
+1999-03-02 09:44:33 dovecot authenticator failed for (xxxx) [127.0.0.1]: 535 Incorrect authentication data (set_id=userx)
diff --git a/test/scripts/3650-Dovecot/3650 b/test/scripts/3650-Dovecot/3650
new file mode 100644 (file)
index 0000000..55b6c1e
--- /dev/null
@@ -0,0 +1,18 @@
+# Dovecot authentication (server only)
+exim -DSERVER=server -bd -oX PORT_D
+****
+client -t3 127.0.0.1 PORT_D
+??? 220
+EHLO xxxx
+??? 250-
+??? 250-
+??? 250-
+??? 250-
+??? 250
+AUTH PLAIN AHVzZXJ4AHNlY3JldA==
+??? 535
+quit
+??? 221
+****
+killdaemon
+no_msglog_check
diff --git a/test/scripts/3650-Dovecot/REQUIRES b/test/scripts/3650-Dovecot/REQUIRES
new file mode 100644 (file)
index 0000000..76218eb
--- /dev/null
@@ -0,0 +1,2 @@
+authenticator dovecot
+running IPv4
diff --git a/test/stderr/3650 b/test/stderr/3650
new file mode 100644 (file)
index 0000000..045fadc
--- /dev/null
@@ -0,0 +1,2 @@
+
+******** SERVER ********
diff --git a/test/stdout/3650 b/test/stdout/3650
new file mode 100644 (file)
index 0000000..87f70de
--- /dev/null
@@ -0,0 +1,21 @@
+Connecting to 127.0.0.1 port 1225 ... connected
+??? 220
+<<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> EHLO xxxx
+??? 250-
+<<< 250-myhost.test.ex Hello xxxx [127.0.0.1]
+??? 250-
+<<< 250-SIZE 52428800
+??? 250-
+<<< 250-PIPELINING
+??? 250-
+<<< 250-AUTH PLAIN
+??? 250
+<<< 250 HELP
+>>> AUTH PLAIN AHVzZXJ4AHNlY3JldA==
+??? 535
+<<< 535 Incorrect authentication data
+>>> quit
+??? 221
+<<< 221 myhost.test.ex closing connection
+End of script