[exim-cvs] common driver structs for auths

Αρχική Σελίδα
Delete this message
Reply to this message
Συντάκτης: Exim Git Commits Mailing List
Ημερομηνία:  
Προς: exim-cvs
Αντικείμενο: [exim-cvs] common driver structs for auths
Gitweb: https://git.exim.org/exim.git/commitdiff/c1a389b4d73ade7dafe1e87484b33ad87ac176a1
Commit:     c1a389b4d73ade7dafe1e87484b33ad87ac176a1
Parent:     1c3dfdb67cc4aabc21db9fcc5a118e7ad82b44ab
Author:     Jeremy Harris <jgh146exb@???>
AuthorDate: Sat Aug 10 23:47:46 2024 +0100
Committer:  Jeremy Harris <jgh146exb@???>
CommitDate: Sat Aug 10 23:47:46 2024 +0100


    common driver structs for auths
---
 src/src/auths/check_serv_cond.c |   2 +-
 src/src/auths/cram_md5.c        |  25 ++++----
 src/src/auths/cram_md5.h        |   2 +-
 src/src/auths/cyrus_sasl.c      |  34 +++++-----
 src/src/auths/cyrus_sasl.h      |   2 +-
 src/src/auths/dovecot.c         |  28 +++++----
 src/src/auths/dovecot.h         |   2 +-
 src/src/auths/external.c        |  22 ++++---
 src/src/auths/external.h        |   2 +-
 src/src/auths/get_data.c        |   4 +-
 src/src/auths/gsasl_exim.c      |  55 ++++++++--------
 src/src/auths/gsasl_exim.h      |   2 +-
 src/src/auths/heimdal_gssapi.c  |  13 ++--
 src/src/auths/heimdal_gssapi.h  |   2 +-
 src/src/auths/plaintext.c       |  24 +++----
 src/src/auths/plaintext.h       |   2 +-
 src/src/auths/spa.c             |  33 +++++-----
 src/src/auths/spa.h             |   2 +-
 src/src/auths/tls.c             |   9 +--
 src/src/auths/tls.h             |   2 +-
 src/src/drtables.c              | 136 +++++++++++++++++++++++-----------------
 src/src/exim.c                  |   6 +-
 src/src/globals.c               |  14 +++--
 src/src/readconf.c              |  19 +++---
 src/src/smtp_in.c               |  35 ++++++-----
 src/src/structs.h               |  20 ++----
 src/src/transports/smtp.c       |  91 ++++++++++++++-------------
 27 files changed, 312 insertions(+), 276 deletions(-)


diff --git a/src/src/auths/check_serv_cond.c b/src/src/auths/check_serv_cond.c
index 16aeebdcb..e43c4c8c8 100644
--- a/src/src/auths/check_serv_cond.c
+++ b/src/src/auths/check_serv_cond.c
@@ -66,7 +66,7 @@ uschar * cond;

 HDEBUG(D_auth)
   {
-  debug_printf("%s authenticator %s:\n", ablock->name, label);
+  debug_printf("%s authenticator %s:\n", ablock->drinst.name, label);
   for (int i = 0; i < AUTH_VARS; i++) if (auth_vars[i])
     debug_printf("  $auth%d = %s\n", i + 1, auth_vars[i]);
   for (int i = 1; i <= expand_nmax; i++)
diff --git a/src/src/auths/cram_md5.c b/src/src/auths/cram_md5.c
index 6d31d4b1d..a64177f2a 100644
--- a/src/src/auths/cram_md5.c
+++ b/src/src/auths/cram_md5.c
@@ -54,7 +54,7 @@ auth_cram_md5_options_block auth_cram_md5_option_defaults = {
 #  ifdef MACRO_PREDEF


 /* Dummy values */
-void auth_cram_md5_init(auth_instance *ablock) {}
+void auth_cram_md5_init(driver_instance* ablock) {}
 int auth_cram_md5_server(auth_instance *ablock, uschar *data) {return 0;}
 int auth_cram_md5_client(auth_instance *ablock, void *sx, int timeout,
     uschar *buffer, int buffsize) {return 0;}
@@ -71,15 +71,18 @@ enable consistency checks to be done, or anything else that needs
 to be set up. */


void
-auth_cram_md5_init(auth_instance *ablock)
+auth_cram_md5_init(driver_instance * a)
{
-auth_cram_md5_options_block *ob =
- (auth_cram_md5_options_block *)(ablock->options_block);
-if (ob->server_secret != NULL) ablock->server = TRUE;
-if (ob->client_secret != NULL)
+auth_instance * ablock = (auth_instance *)a;
+auth_cram_md5_options_block * ob =
+ (auth_cram_md5_options_block *)(a->options_block);
+
+if (ob->server_secret)
+ ablock->server = TRUE;
+if (ob->client_secret)
{
ablock->client = TRUE;
- if (ob->client_name == NULL) ob->client_name = primary_hostname;
+ if (!ob->client_name) ob->client_name = primary_hostname;
}
}

@@ -169,8 +172,7 @@ md5_end(&base, md5secret, 16, digestptr);
 int
 auth_cram_md5_server(auth_instance * ablock, uschar * data)
 {
-auth_cram_md5_options_block * ob =
-  (auth_cram_md5_options_block *)(ablock->options_block);
+auth_cram_md5_options_block * ob = ablock->drinst.options_block;
 uschar * challenge = string_sprintf("<%d.%ld@%s>", getpid(),
     (long int) time(NULL), primary_hostname);
 uschar * clear, * secret;
@@ -269,8 +271,7 @@ auth_cram_md5_client(
   uschar *buffer,                        /* for reading response */
   int buffsize)                          /* size of buffer */
 {
-auth_cram_md5_options_block *ob =
-  (auth_cram_md5_options_block *)(ablock->options_block);
+auth_cram_md5_options_block * ob = ablock->drinst.options_block;
 uschar *secret = expand_string(ob->client_secret);
 uschar *name = expand_string(ob->client_name);
 uschar *challenge, *p;
@@ -290,7 +291,7 @@ if (!secret || !name)
   string_format(buffer, buffsize, "expansion of \"%s\" failed in "
     "%s authenticator: %s",
     !secret ? ob->client_secret : ob->client_name,
-    ablock->name, expand_string_message);
+    ablock->drinst.name, expand_string_message);
   return ERROR;
   }


diff --git a/src/src/auths/cram_md5.h b/src/src/auths/cram_md5.h
index 984bc14c7..f3a7fb944 100644
--- a/src/src/auths/cram_md5.h
+++ b/src/src/auths/cram_md5.h
@@ -25,7 +25,7 @@ extern auth_cram_md5_options_block auth_cram_md5_option_defaults;

/* The entry points for the mechanism */

-extern void auth_cram_md5_init(auth_instance *);
+extern void auth_cram_md5_init(driver_instance*);
extern int auth_cram_md5_server(auth_instance *, uschar *);
extern int auth_cram_md5_client(auth_instance *, void *, int, uschar *, int);

diff --git a/src/src/auths/cyrus_sasl.c b/src/src/auths/cyrus_sasl.c
index 8488fba19..8266e2319 100644
--- a/src/src/auths/cyrus_sasl.c
+++ b/src/src/auths/cyrus_sasl.c
@@ -68,7 +68,7 @@ auth_cyrus_sasl_options_block auth_cyrus_sasl_option_defaults = {
#ifdef MACRO_PREDEF

/* Dummy values */
-void auth_cyrus_sasl_init(auth_instance *ablock) {}
+void auth_cyrus_sasl_init(driver_instance *ablock) {}
int auth_cyrus_sasl_server(auth_instance *ablock, uschar *data) {return 0;}
int auth_cyrus_sasl_client(auth_instance *ablock, void * sx,
int timeout, uschar *buffer, int buffsize) {return 0;}
@@ -106,10 +106,10 @@ return SASL_FAIL;
/* Here's the real function */

 void
-auth_cyrus_sasl_init(auth_instance *ablock)
+auth_cyrus_sasl_init(driver_instance * a)
 {
-auth_cyrus_sasl_options_block *ob =
-  (auth_cyrus_sasl_options_block *)(ablock->options_block);
+auth_instance * ablock = (auth_instance *)a;
+auth_cyrus_sasl_options_block * ob = a->options_block;
 const uschar *list, *listptr, *buffer;
 int rc, i;
 unsigned int len;
@@ -129,14 +129,14 @@ if (!ob->server_mech) ob->server_mech = string_copy(ablock->public_name);
 if (!(expanded_hostname = expand_string(ob->server_hostname)))
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
       "couldn't expand server_hostname [%s]: %s",
-      ablock->name, ob->server_hostname, expand_string_message);
+      a->name, ob->server_hostname, expand_string_message);


 realm_expanded = NULL;
 if (  ob->server_realm
    && !(realm_expanded = CS expand_string(ob->server_realm)))
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
       "couldn't expand server_realm [%s]: %s",
-      ablock->name, ob->server_realm, expand_string_message);
+      a->name, ob->server_realm, expand_string_message);


/* we're going to initialise the library to check that there is an
authenticator of type whatever mechanism we're using */
@@ -146,16 +146,16 @@ cbs[0].context = ob->server_mech;

 if ((rc = sasl_server_init(cbs, "exim")) != SASL_OK)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
-      "couldn't initialise Cyrus SASL library.", ablock->name);
+      "couldn't initialise Cyrus SASL library.", a->name);


 if ((rc = sasl_server_new(CS ob->server_service, CS expanded_hostname,
                    realm_expanded, NULL, NULL, NULL, 0, &conn)) != SASL_OK)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
-      "couldn't initialise Cyrus SASL server connection.", ablock->name);
+      "couldn't initialise Cyrus SASL server connection.", a->name);


 if ((rc = sasl_listmech(conn, NULL, "", ":", "", CCSS &list, &len, &i)) != SASL_OK)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
-      "couldn't get Cyrus SASL mechanism list.", ablock->name);
+      "couldn't get Cyrus SASL mechanism list.", a->name);


i = ':';
listptr = list;
@@ -181,11 +181,11 @@ while ( (buffer = string_nextinlist(&listptr, &i, NULL, 0))

 if (!buffer)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
-      "Cyrus SASL doesn't know about mechanism %s.", ablock->name, ob->server_mech);
+      "Cyrus SASL doesn't know about mechanism %s.", a->name, ob->server_mech);


store_reset(rs_point);

-HDEBUG(D_auth) debug_printf("Cyrus SASL driver %s: %s initialised\n", ablock->name, ablock->public_name);
+HDEBUG(D_auth) debug_printf("Cyrus SASL driver %s: %s initialised\n", a->name, ablock->public_name);

 /* make sure that if we get here then we're allowed to advertise. */
 ablock->server = TRUE;
@@ -206,8 +206,8 @@ within a shortlived child */
 int
 auth_cyrus_sasl_server(auth_instance * ablock, uschar * data)
 {
-auth_cyrus_sasl_options_block * ob =
-  (auth_cyrus_sasl_options_block *)(ablock->options_block);
+auth_cyrus_sasl_options_block * ob = ablock->drinst.options_block;
+const uschar * auname = ablock->drinst.name;
 uschar * output, * out2, * input, * clear, * hname;
 uschar * debug = NULL;   /* Stops compiler complaining */
 sasl_callback_t cbs[] = {{SASL_CB_LIST_END, NULL, NULL}};
@@ -380,7 +380,7 @@ for (rc = SASL_CONTINUE; rc == SASL_CONTINUE; )
       debug_printf("Cyrus SASL library will not tell us the username: %s\n",
       sasl_errstring(rc, NULL, NULL));
     log_write(0, LOG_REJECT, "%s authenticator (%s): "
-       "Cyrus SASL username fetch problem: %s", ablock->name, ob->server_mech,
+       "Cyrus SASL username fetch problem: %s", auname, ob->server_mech,
        sasl_errstring(rc, NULL, NULL));
     sasl_dispose(&conn);
     sasl_done();
@@ -399,7 +399,7 @@ for (rc = SASL_CONTINUE; rc == SASL_CONTINUE; )
       HDEBUG(D_auth)
     debug_printf("Cyrus SASL permanent failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
       log_write(0, LOG_REJECT, "%s authenticator (%s): "
-     "Cyrus SASL permanent failure: %s", ablock->name, ob->server_mech,
+     "Cyrus SASL permanent failure: %s", auname, ob->server_mech,
      sasl_errstring(rc, NULL, NULL));
       sasl_dispose(&conn);
       sasl_done();
@@ -429,7 +429,7 @@ for (rc = SASL_CONTINUE; rc == SASL_CONTINUE; )
       debug_printf("Cyrus SASL library will not tell us the SSF: %s\n",
           sasl_errstring(rc, NULL, NULL));
     log_write(0, LOG_REJECT, "%s authenticator (%s): "
-        "Cyrus SASL SSF value not available: %s", ablock->name, ob->server_mech,
+        "Cyrus SASL SSF value not available: %s", auname, ob->server_mech,
         sasl_errstring(rc, NULL, NULL));
     sasl_dispose(&conn);
     sasl_done();
@@ -443,7 +443,7 @@ for (rc = SASL_CONTINUE; rc == SASL_CONTINUE; )
     HDEBUG(D_auth)
       debug_printf("Exim does not implement SASL wrapping (needed for SSF %d).\n", negotiated_ssf);
     log_write(0, LOG_REJECT, "%s authenticator (%s): "
-        "Cyrus SASL SSF %d not supported by Exim", ablock->name, ob->server_mech, negotiated_ssf);
+        "Cyrus SASL SSF %d not supported by Exim", auname, ob->server_mech, negotiated_ssf);
     sasl_dispose(&conn);
     sasl_done();
     return FAIL;
diff --git a/src/src/auths/cyrus_sasl.h b/src/src/auths/cyrus_sasl.h
index 05071b6e5..4c5bd8b25 100644
--- a/src/src/auths/cyrus_sasl.h
+++ b/src/src/auths/cyrus_sasl.h
@@ -29,7 +29,7 @@ extern auth_cyrus_sasl_options_block auth_cyrus_sasl_option_defaults;


/* The entry points for the mechanism */

-extern void auth_cyrus_sasl_init(auth_instance *);
+extern void auth_cyrus_sasl_init(driver_instance *);
extern int auth_cyrus_sasl_server(auth_instance *, uschar *);
extern int auth_cyrus_sasl_client(auth_instance *, void *, int, uschar *, int);
extern gstring * auth_cyrus_sasl_version_report(gstring *);
diff --git a/src/src/auths/dovecot.c b/src/src/auths/dovecot.c
index 8b80f2c3f..fdfdbc749 100644
--- a/src/src/auths/dovecot.c
+++ b/src/src/auths/dovecot.c
@@ -54,8 +54,10 @@ The cost is the length of an array of pointers on the stack.

 /* Options specific to the authentication mechanism. */
 optionlist auth_dovecot_options[] = {
-  { "server_socket", opt_stringptr, OPT_OFF(auth_dovecot_options_block, server_socket) },
-/*{ "server_tls", opt_bool, OPT_OFF(auth_dovecot_options_block, server_tls) },*/
+  { "server_socket", opt_stringptr,
+    OPT_OFF(auth_dovecot_options_block, server_socket) },
+/*{ "server_tls", opt_bool,
+    OPT_OFF(auth_dovecot_options_block, server_tls) },*/
 };


/* Size of the options list. An extern variable has to be used so that its
@@ -76,7 +78,7 @@ auth_dovecot_options_block auth_dovecot_option_defaults = {
#ifdef MACRO_PREDEF

/* Dummy values */
-void auth_dovecot_init(auth_instance *ablock) {}
+void auth_dovecot_init(driver_instance *ablock) {}
int auth_dovecot_server(auth_instance *ablock, uschar *data) {return 0;}
int auth_dovecot_client(auth_instance *ablock, void * sx,
int timeout, uschar *buffer, int buffsize) {return 0;}
@@ -100,14 +102,19 @@ enable consistency checks to be done, or anything else that needs
to be set up. */

 void
-auth_dovecot_init(auth_instance * ablock)
+auth_dovecot_init(driver_instance * a)
 {
+auth_instance * ablock = (auth_instance *)a;
 auth_dovecot_options_block * ob =
-       (auth_dovecot_options_block *)(ablock->options_block);
-
-if (!ablock->public_name) ablock->public_name = ablock->name;
-if (ob->server_socket) ablock->server = TRUE;
-else DEBUG(D_auth) debug_printf("Dovecot auth driver: no server_socket for %s\n", ablock->public_name);
+       (auth_dovecot_options_block *)(a->options_block);
+
+if (!ablock->public_name)
+  ablock->public_name = a->name;
+if (ob->server_socket)
+  ablock->server = TRUE;
+else DEBUG(D_auth)
+  debug_printf("Dovecot auth driver: no server_socket for %s\n",
+          ablock->public_name);
 ablock->client = FALSE;
 }


@@ -250,8 +257,7 @@ return s;
 int
 auth_dovecot_server(auth_instance * ablock, uschar * data)
 {
-auth_dovecot_options_block *ob =
-       (auth_dovecot_options_block *) ablock->options_block;
+auth_dovecot_options_block * ob = ablock->drinst.options_block;
 uschar buffer[DOVECOT_AUTH_MAXLINELEN];
 uschar *args[DOVECOT_AUTH_MAXFIELDCOUNT];
 uschar *auth_command;
diff --git a/src/src/auths/dovecot.h b/src/src/auths/dovecot.h
index 74c451930..a9cc0a2b4 100644
--- a/src/src/auths/dovecot.h
+++ b/src/src/auths/dovecot.h
@@ -25,7 +25,7 @@ extern auth_dovecot_options_block auth_dovecot_option_defaults;


/* The entry points for the mechanism */

-extern void auth_dovecot_init(auth_instance *);
+extern void auth_dovecot_init(driver_instance *);
extern int auth_dovecot_server(auth_instance *, uschar *);

/* End of dovecot.h */
diff --git a/src/src/auths/external.c b/src/src/auths/external.c
index a8e04310f..137d1e043 100644
--- a/src/src/auths/external.c
+++ b/src/src/auths/external.c
@@ -44,7 +44,7 @@ auth_external_options_block auth_external_option_defaults = {
#ifdef MACRO_PREDEF

/* Dummy values */
-void auth_external_init(auth_instance *ablock) {}
+void auth_external_init(driver_instance *ablock) {}
int auth_external_server(auth_instance *ablock, uschar *data) {return 0;}
int auth_external_client(auth_instance *ablock, void * sx,
int timeout, uschar *buffer, int buffsize) {return 0;}
@@ -63,12 +63,17 @@ enable consistency checks to be done, or anything else that needs
to be set up. */

void
-auth_external_init(auth_instance *ablock)
+auth_external_init(driver_instance * a)
{
-auth_external_options_block * ob = (auth_external_options_block *)ablock->options_block;
-if (!ablock->public_name) ablock->public_name = ablock->name;
-if (ablock->server_condition) ablock->server = TRUE;
-if (ob->client_send) ablock->client = TRUE;
+auth_instance * ablock = (auth_instance *)a;
+auth_external_options_block * ob = a->options_block;
+
+if (!ablock->public_name)
+ ablock->public_name = a->name;
+if (ablock->server_condition)
+ ablock->server = TRUE;
+if (ob->client_send)
+ ablock->client = TRUE;
}


@@ -82,7 +87,7 @@ if (ob->client_send) ablock->client = TRUE;
int
auth_external_server(auth_instance * ablock, uschar * data)
{
-auth_external_options_block * ob = (auth_external_options_block *)ablock->options_block;
+auth_external_options_block * ob = ablock->drinst.options_block;
int rc;

 /* If data was supplied on the AUTH command, decode it, and split it up into
@@ -138,8 +143,7 @@ auth_external_client(
   uschar *buffer,                        /* buffer for reading response */
   int buffsize)                          /* size of buffer */
 {
-auth_external_options_block *ob =
-  (auth_external_options_block *)(ablock->options_block);
+auth_external_options_block * ob = ablock->drinst.options_block;
 const uschar * text = ob->client_send;
 int rc;


diff --git a/src/src/auths/external.h b/src/src/auths/external.h
index 0a9b0b50e..98475d670 100644
--- a/src/src/auths/external.h
+++ b/src/src/auths/external.h
@@ -26,7 +26,7 @@ extern auth_external_options_block auth_external_option_defaults;

/* The entry points for the mechanism */

-extern void auth_external_init(auth_instance *);
+extern void auth_external_init(driver_instance *);
extern int auth_external_server(auth_instance *, uschar *);
extern int auth_external_client(auth_instance *, void *, int, uschar *, int);

diff --git a/src/src/auths/get_data.c b/src/src/auths/get_data.c
index 4b79cbfa4..3a9a0a46a 100644
--- a/src/src/auths/get_data.c
+++ b/src/src/auths/get_data.c
@@ -166,7 +166,7 @@ if (!ss)
     return CANCELLED;
     }
   string_format(buffer, buffsize, "expansion of \"%s\" failed in %s "
-    "authenticator: %s", *inout, ablock->name, expand_string_message);
+    "authenticator: %s", *inout, ablock->drinst.name, expand_string_message);
   return ERROR;
   }


@@ -225,7 +225,7 @@ if (flags & AUTH_ITEM_LAST)
   if (smtp_write_command(sx, SCMD_FLUSH, "*\r\n") >= 0)
     (void)smtp_read_response(sx, US buffer, buffsize, '2', timeout);
   string_format(buffer, buffsize, "Too few items in client_send in %s "
-    "authenticator", ablock->name);
+    "authenticator", ablock->drinst.name);
   return ERROR;
   }


diff --git a/src/src/auths/gsasl_exim.c b/src/src/auths/gsasl_exim.c
index 07b91e790..55ac15b4b 100644
--- a/src/src/auths/gsasl_exim.c
+++ b/src/src/auths/gsasl_exim.c
@@ -121,7 +121,7 @@ auth_gsasl_options_block auth_gsasl_option_defaults = {
# include "../macro_predef.h"

/* Dummy values */
-void auth_gsasl_init(auth_instance *ablock) {}
+void auth_gsasl_init(driver_instance *ablock) {}
int auth_gsasl_server(auth_instance *ablock, uschar *data) {return 0;}
int auth_gsasl_client(auth_instance *ablock, void * sx,
int timeout, uschar *buffer, int buffsize) {return 0;}
@@ -173,12 +173,12 @@ enable consistency checks to be done, or anything else that needs
to be set up. */

void
-auth_gsasl_init(auth_instance *ablock)
+auth_gsasl_init(driver_instance * a)
{
+auth_instance * ablock = (auth_instance *)a;
+auth_gsasl_options_block * ob = a->options_block;
static char * once = NULL;
int rc;
-auth_gsasl_options_block *ob =
- (auth_gsasl_options_block *)(ablock->options_block);

 /* As per existing Cyrus glue, use the authenticator's public name as
 the default for the mechanism name; we don't handle multiple mechanisms
@@ -195,7 +195,7 @@ if (!gsasl_ctx)
   if ((rc = gsasl_init(&gsasl_ctx)) != GSASL_OK)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
           "couldn't initialise GNU SASL library: %s (%s)",
-          ablock->name, gsasl_strerror_name(rc), gsasl_strerror(rc));
+          a->name, gsasl_strerror_name(rc), gsasl_strerror(rc));


   gsasl_callback_set(gsasl_ctx, main_callback);
   }
@@ -207,7 +207,7 @@ HDEBUG(D_auth) if (!once)
   if ((rc = gsasl_server_mechlist(gsasl_ctx, &once)) != GSASL_OK)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
           "failed to retrieve list of mechanisms: %s (%s)",
-          ablock->name,  gsasl_strerror_name(rc), gsasl_strerror(rc));
+          a->name,  gsasl_strerror_name(rc), gsasl_strerror(rc));


   debug_printf("GNU SASL supports: %s\n", once);
   }
@@ -215,7 +215,7 @@ HDEBUG(D_auth) if (!once)
 if (!gsasl_client_support_p(gsasl_ctx, CCS ob->server_mech))
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
         "GNU SASL does not support mechanism \"%s\"",
-        ablock->name, ob->server_mech);
+        a->name, ob->server_mech);


 if (ablock->server_condition)
   ablock->server = TRUE;
@@ -235,7 +235,7 @@ else if(  ob->server_mech
   ablock->server = FALSE;
   HDEBUG(D_auth) debug_printf("%s authenticator:  "
         "Need server_condition for %s mechanism\n",
-        ablock->name, ob->server_mech);
+        a->name, ob->server_mech);
   }


 /* This does *not* scale to new SASL mechanisms.  Need a better way to ask
@@ -247,7 +247,7 @@ if (  !ob->server_realm
   ablock->server = FALSE;
   HDEBUG(D_auth) debug_printf("%s authenticator:  "
         "Need server_realm for %s mechanism\n",
-        ablock->name, ob->server_mech);
+        a->name, ob->server_mech);
   }


 ablock->client = ob->client_username && ob->client_password;
@@ -309,7 +309,7 @@ else if (cb_state->currently == CURRENTLY_SERVER)
   rc = server_callback(ctx, sctx, prop, cb_state->ablock);
 else
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
-      "unhandled callback state, bug in Exim", cb_state->ablock->name);
+      "unhandled callback state, bug in Exim", cb_state->ablock->drinst.name);
   /* NOTREACHED */


callback_loop = 0;
@@ -384,17 +384,17 @@ gsasl_property_set(sctx, propcode, CCS val);
int
auth_gsasl_server(auth_instance * ablock, uschar * initial_data)
{
+auth_gsasl_options_block * ob = ablock->drinst.options_block;
+const uschar * auname = ablock->drinst.name;
uschar * tmps;
char * to_send, * received;
Gsasl_session * sctx = NULL;
-auth_gsasl_options_block * ob =
- (auth_gsasl_options_block *)(ablock->options_block);
struct callback_exim_state cb_state;
int rc, auth_result, exim_error, exim_error_override;

 HDEBUG(D_auth)
   debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
-      ablock->name, ob->server_mech);
+      auname, ob->server_mech);


 #ifndef DISABLE_TLS
 if (tls_in.channelbinding && ob->server_channelbinding)
@@ -473,7 +473,7 @@ if (tls_in.channelbinding)
   if (ob->server_channelbinding)
     {
     HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
-    ablock->name);
+    auname);
 # ifndef CHANNELBIND_HACK
     preload_prop(sctx,
 #  ifdef EXIM_GSASL_HAVE_EXPORTER
@@ -486,12 +486,12 @@ if (tls_in.channelbinding)
   else
     HDEBUG(D_auth)
       debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
-      ablock->name);
+      auname);
   }
 else
   HDEBUG(D_auth)
     debug_printf("Auth %s: no channel-binding data available\n",
-    ablock->name);
+    auname);
 #endif


 checked_server_condition = FALSE;
@@ -524,7 +524,7 @@ do {
       gsasl_strerror_name(rc), gsasl_strerror(rc));
       log_write(0, LOG_REJECT, "%s authenticator (%s):\n  "
       "GNU SASL permanent failure: %s (%s)",
-      ablock->name, ob->server_mech,
+      auname, ob->server_mech,
       gsasl_strerror_name(rc), gsasl_strerror(rc));
       if (rc == GSASL_BASE64_ERROR)
     exim_error_override = BAD64;
@@ -607,7 +607,7 @@ switch (exim_rc)
   case FAIL:    return GSASL_AUTHENTICATION_ERROR;
   default:    log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
           "Unhandled return from checking %s: %d",
-          ablock->name, label, exim_rc);
+          ablock->drinst.name, label, exim_rc);
   }


/* NOTREACHED */
@@ -671,14 +671,13 @@ static int
server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop,
auth_instance *ablock)
{
+auth_gsasl_options_block * ob = ablock->drinst.options_block;
char * tmps;
uschar * s;
int cbrc = GSASL_NO_CALLBACK;
-auth_gsasl_options_block * ob =
- (auth_gsasl_options_block *)(ablock->options_block);

 HDEBUG(D_auth) debug_printf("GNU SASL callback %s for %s/%s as server\n",
-        gsasl_prop_code_to_name(prop), ablock->name, ablock->public_name);
+      gsasl_prop_code_to_name(prop), ablock->drinst.name, ablock->public_name);


 for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
 expand_nmax = 0;
@@ -846,8 +845,8 @@ auth_gsasl_client(
   uschar * buffer,            /* buffer for reading response */
   int buffsize)                /* size of buffer */
 {
-auth_gsasl_options_block * ob =
-  (auth_gsasl_options_block *)(ablock->options_block);
+auth_gsasl_options_block * ob = ablock->drinst.options_block;
+const uschar * auname = ablock->drinst.name;
 Gsasl_session * sctx = NULL;
 struct callback_exim_state cb_state;
 uschar * s;
@@ -856,7 +855,7 @@ int rc, yield = FAIL;


 HDEBUG(D_auth)
   debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
-      ablock->name, ob->server_mech);
+      auname, ob->server_mech);


*buffer = 0;

@@ -910,7 +909,7 @@ if (tls_out.channelbinding)
   if (ob->client_channelbinding)
     {
     HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
-    ablock->name);
+    auname);
 # ifndef CHANNELBIND_HACK
     preload_prop(sctx,
 #  ifdef EXIM_GSASL_HAVE_EXPORTER
@@ -923,7 +922,7 @@ if (tls_out.channelbinding)
   else
     HDEBUG(D_auth)
       debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
-      ablock->name);
+      auname);
 #endif


 /* Run the SASL conversation with the server */
@@ -999,7 +998,7 @@ static int
 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock)
 {
 HDEBUG(D_auth) debug_printf("GNU SASL callback %s for %s/%s as client\n",
-        gsasl_prop_code_to_name(prop), ablock->name, ablock->public_name);
+      gsasl_prop_code_to_name(prop), ablock->drinst.name, ablock->public_name);
 switch (prop)
   {
 #ifdef EXIM_GSASL_HAVE_EXPORTER
@@ -1019,7 +1018,7 @@ switch (prop)
   case GSASL_SCRAM_SALTED_PASSWORD:
     {
     uschar * client_spassword =
-      ((auth_gsasl_options_block *) ablock->options_block)->client_spassword;
+      ((auth_gsasl_options_block *) ablock->drinst.options_block)->client_spassword;
     uschar dummy[4];
     HDEBUG(D_auth) if (!client_spassword)
       debug_printf(" client_spassword option unset\n");
diff --git a/src/src/auths/gsasl_exim.h b/src/src/auths/gsasl_exim.h
index a56535710..180d4c848 100644
--- a/src/src/auths/gsasl_exim.h
+++ b/src/src/auths/gsasl_exim.h
@@ -44,7 +44,7 @@ extern auth_gsasl_options_block auth_gsasl_option_defaults;


/* The entry points for the mechanism */

-extern void auth_gsasl_init(auth_instance *);
+extern void auth_gsasl_init(driver_instance *);
 extern int auth_gsasl_server(auth_instance *, uschar *);
 extern int auth_gsasl_client(auth_instance *, void *,
                 int, uschar *, int);
diff --git a/src/src/auths/heimdal_gssapi.c b/src/src/auths/heimdal_gssapi.c
index 686b2d98d..a5eef8d95 100644
--- a/src/src/auths/heimdal_gssapi.c
+++ b/src/src/auths/heimdal_gssapi.c
@@ -82,7 +82,7 @@ auth_heimdal_gssapi_options_block auth_heimdal_gssapi_option_defaults = {
 #ifdef MACRO_PREDEF


/* Dummy values */
-void auth_heimdal_gssapi_init(auth_instance *ablock) {}
+void auth_heimdal_gssapi_init(driver_instance *ablock) {}
int auth_heimdal_gssapi_server(auth_instance *ablock, uschar *data) {return 0;}
int auth_heimdal_gssapi_client(auth_instance *ablock, void * sx,
int timeout, uschar *buffer, int buffsize) {return 0;}
@@ -117,8 +117,11 @@ in the init, we mostly just use raw krb5 methods so that we can report
the keytab contents, for -D+auth debugging. */

void
-auth_heimdal_gssapi_init(auth_instance *ablock)
+auth_heimdal_gssapi_init(driver_instance * a)
{
+auth_instance * ablock = (auth_instance *)a;
+auth_heimdal_gssapi_options_block * ob =
+ (auth_heimdal_gssapi_options_block *)(a->options_block);
krb5_context context;
krb5_keytab keytab;
krb5_kt_cursor cursor;
@@ -126,8 +129,6 @@ krb5_keytab_entry entry;
krb5_error_code krc;
char *principal, *enctype_s;
const char *k_keytab_typed_name = NULL;
-auth_heimdal_gssapi_options_block *ob =
- (auth_heimdal_gssapi_options_block *)(ablock->options_block);

ablock->server = FALSE;
ablock->client = FALSE;
@@ -226,6 +227,8 @@ gss_buffer_desc / *gss_buffer_t: hold/point-to size_t .length & void *value
int
auth_heimdal_gssapi_server(auth_instance *ablock, uschar *initial_data)
{
+auth_heimdal_gssapi_options_block * ob =
+ (auth_heimdal_gssapi_options_block *)(ablock->drinfo.options_block);
gss_name_t gclient = GSS_C_NO_NAME;
gss_name_t gserver = GSS_C_NO_NAME;
gss_cred_id_t gcred = GSS_C_NO_CREDENTIAL;
@@ -238,8 +241,6 @@ gss_OID mech_type;
OM_uint32 maj_stat, min_stat;
int step, error_out;
uschar *tmp1, *tmp2, *from_client;
-auth_heimdal_gssapi_options_block *ob =
- (auth_heimdal_gssapi_options_block *)(ablock->options_block);
BOOL handled_empty_ir;
rmark store_reset_point;
uschar *keytab;
diff --git a/src/src/auths/heimdal_gssapi.h b/src/src/auths/heimdal_gssapi.h
index 6c9b24298..c438aae91 100644
--- a/src/src/auths/heimdal_gssapi.h
+++ b/src/src/auths/heimdal_gssapi.h
@@ -32,7 +32,7 @@ extern auth_heimdal_gssapi_options_block auth_heimdal_gssapi_option_defaults;

/* The entry points for the mechanism */

-extern void auth_heimdal_gssapi_init(auth_instance *);
+extern void auth_heimdal_gssapi_init(driver_instance *);
extern int auth_heimdal_gssapi_server(auth_instance *, uschar *);
extern int auth_heimdal_gssapi_client(auth_instance *, void *, int, uschar *, int);
extern void auth_heimdal_gssapi_version_report(BOOL);
diff --git a/src/src/auths/plaintext.c b/src/src/auths/plaintext.c
index 7f59e4c7d..26ac4aeff 100644
--- a/src/src/auths/plaintext.c
+++ b/src/src/auths/plaintext.c
@@ -42,7 +42,7 @@ auth_plaintext_options_block auth_plaintext_option_defaults = {
#ifdef MACRO_PREDEF

 /* Dummy values */
-void auth_plaintext_init(auth_instance *ablock) {}
+void auth_plaintext_init(driver_instance *ablock) {}
 int auth_plaintext_server(auth_instance *ablock, uschar *data) {return 0;}
 int auth_plaintext_client(auth_instance *ablock, void * sx, int timeout,
     uschar *buffer, int buffsize) {return 0;}
@@ -60,13 +60,17 @@ enable consistency checks to be done, or anything else that needs
 to be set up. */


void
-auth_plaintext_init(auth_instance *ablock)
+auth_plaintext_init(driver_instance * a)
{
-auth_plaintext_options_block *ob =
- (auth_plaintext_options_block *)(ablock->options_block);
-if (!ablock->public_name) ablock->public_name = ablock->name;
-if (ablock->server_condition) ablock->server = TRUE;
-if (ob->client_send) ablock->client = TRUE;
+auth_instance * ablock = (auth_instance *)a;
+auth_plaintext_options_block * ob = a->options_block;
+
+if (!ablock->public_name)
+ ablock->public_name = ablock->drinst.name;
+if (ablock->server_condition)
+ ablock->server = TRUE;
+if (ob->client_send)
+ ablock->client = TRUE;
}


@@ -80,8 +84,7 @@ if (ob->client_send) ablock->client = TRUE;
 int
 auth_plaintext_server(auth_instance * ablock, uschar * data)
 {
-auth_plaintext_options_block * ob =
-  (auth_plaintext_options_block *)(ablock->options_block);
+auth_plaintext_options_block * ob = ablock->drinst.options_block;
 const uschar * prompts = ob->server_prompts;
 uschar * s;
 int number = 1;
@@ -143,8 +146,7 @@ auth_plaintext_client(
   uschar *buffer,                        /* buffer for reading response */
   int buffsize)                          /* size of buffer */
 {
-auth_plaintext_options_block *ob =
-  (auth_plaintext_options_block *)(ablock->options_block);
+auth_plaintext_options_block * ob = ablock->drinst.options_block;
 const uschar * text = ob->client_send;
 const uschar * s;
 int sep = 0;
diff --git a/src/src/auths/plaintext.h b/src/src/auths/plaintext.h
index fdf0feb93..ab820ba51 100644
--- a/src/src/auths/plaintext.h
+++ b/src/src/auths/plaintext.h
@@ -25,7 +25,7 @@ extern auth_plaintext_options_block auth_plaintext_option_defaults;


/* The entry points for the mechanism */

-extern void auth_plaintext_init(auth_instance *);
+extern void auth_plaintext_init(driver_instance *);
extern int auth_plaintext_server(auth_instance *, uschar *);
extern int auth_plaintext_client(auth_instance *, void *, int, uschar *, int);

diff --git a/src/src/auths/spa.c b/src/src/auths/spa.c
index e13798f0e..09d4e43a6 100644
--- a/src/src/auths/spa.c
+++ b/src/src/auths/spa.c
@@ -78,7 +78,7 @@ auth_spa_options_block auth_spa_option_defaults = {
#ifdef MACRO_PREDEF

 /* Dummy values */
-void auth_spa_init(auth_instance *ablock) {}
+void auth_spa_init(driver_instance *ablock) {}
 int auth_spa_server(auth_instance *ablock, uschar *data) {return 0;}
 int auth_spa_client(auth_instance *ablock, void * sx, int timeout,
     uschar *buffer, int buffsize) {return 0;}
@@ -97,21 +97,22 @@ enable consistency checks to be done, or anything else that needs
 to be set up. */


void
-auth_spa_init(auth_instance *ablock)
+auth_spa_init(driver_instance * a)
{
-auth_spa_options_block *ob =
- (auth_spa_options_block *)(ablock->options_block);
+auth_instance * ablock = (auth_instance *)a;
+auth_spa_options_block * ob = a->options_block;

/* The public name defaults to the authenticator name */

-if (ablock->public_name == NULL) ablock->public_name = ablock->name;
+if (!ablock->public_name)
+ ablock->public_name = ablock->drinst.name;

/* Both username and password must be set for a client */

 if ((ob->spa_username == NULL) != (ob->spa_password == NULL))
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:\n  "
       "one of client_username and client_password cannot be set without "
-      "the other", ablock->name);
+      "the other", ablock->drinst.name);
 ablock->client = ob->spa_username != NULL;


 /* For a server we have just one option */
@@ -135,7 +136,7 @@ ablock->server = ob->spa_serverpassword != NULL;
 int
 auth_spa_server(auth_instance *ablock, uschar *data)
 {
-auth_spa_options_block *ob = (auth_spa_options_block *)(ablock->options_block);
+auth_spa_options_block * ob = ablock->drinst.options_block;
 uint8x lmRespData[24];
 uint8x ntRespData[24];
 SPAAuthRequest request;
@@ -281,8 +282,8 @@ auth_spa_client(
   uschar *buffer,                        /* buffer for reading response */
   int buffsize)                          /* size of buffer */
 {
-auth_spa_options_block *ob =
-       (auth_spa_options_block *)(ablock->options_block);
+auth_spa_options_block * ob = ablock->drinst.options_block;
+const uschar * auname = ablock->drinst.name;
 SPAAuthRequest   request;
 SPAAuthChallenge challenge;
 SPAAuthResponse  response;
@@ -297,7 +298,7 @@ if (!(username = expand_string(ob->spa_username)))
   {
   if (f.expand_string_forcedfail) return CANCELLED;
   string_format(buffer, buffsize, "expansion of \"%s\" failed in %s "
-   "authenticator: %s", ob->spa_username, ablock->name,
+   "authenticator: %s", ob->spa_username, auname,
    expand_string_message);
   return ERROR;
   }
@@ -306,7 +307,7 @@ if (!(password = expand_string(ob->spa_password)))
   {
   if (f.expand_string_forcedfail) return CANCELLED;
   string_format(buffer, buffsize, "expansion of \"%s\" failed in %s "
-   "authenticator: %s", ob->spa_password, ablock->name,
+   "authenticator: %s", ob->spa_password, auname,
    expand_string_message);
   return ERROR;
   }
@@ -316,7 +317,7 @@ if (ob->spa_domain)
     {
     if (f.expand_string_forcedfail) return CANCELLED;
     string_format(buffer, buffsize, "expansion of \"%s\" failed in %s "
-          "authenticator: %s", ob->spa_domain, ablock->name,
+          "authenticator: %s", ob->spa_domain, auname,
           expand_string_message);
     return ERROR;
     }
@@ -330,12 +331,12 @@ if (smtp_write_command(sx, SCMD_FLUSH, "AUTH %s\r\n", ablock->public_name) < 0)
 if (!smtp_read_response(sx, US buffer, buffsize, '3', timeout))
   return FAIL;


-DSPA("\n\n%s authenticator: using domain %s\n\n", ablock->name, domain);
+DSPA("\n\n%s authenticator: using domain %s\n\n", auname, domain);

spa_build_auth_request(&request, username, domain);
spa_bits_to_base64(US msgbuf, US &request, spa_request_length(&request));

-DSPA("\n\n%s authenticator: sending request (%s)\n\n", ablock->name, msgbuf);
+DSPA("\n\n%s authenticator: sending request (%s)\n\n", auname, msgbuf);

/* send the encrypted password */
if (smtp_write_command(sx, SCMD_FLUSH, "%s\r\n", msgbuf) < 0)
@@ -346,12 +347,12 @@ if (!smtp_read_response(sx, US buffer, buffsize, '3', timeout))
return FAIL;

/* convert the challenge into the challenge struct */
-DSPA("\n\n%s authenticator: challenge (%s)\n\n", ablock->name, buffer + 4);
+DSPA("\n\n%s authenticator: challenge (%s)\n\n", auname, buffer + 4);
spa_base64_to_bits(CS (&challenge), sizeof(challenge), CCS (buffer + 4));

spa_build_auth_response(&challenge, &response, username, password);
spa_bits_to_base64(US msgbuf, US &response, spa_request_length(&response));
-DSPA("\n\n%s authenticator: challenge response (%s)\n\n", ablock->name, msgbuf);
+DSPA("\n\n%s authenticator: challenge response (%s)\n\n", auname, msgbuf);

/* send the challenge response */
if (smtp_write_command(sx, SCMD_FLUSH, "%s\r\n", msgbuf) < 0)
diff --git a/src/src/auths/spa.h b/src/src/auths/spa.h
index 625a252d6..916a57f81 100644
--- a/src/src/auths/spa.h
+++ b/src/src/auths/spa.h
@@ -32,7 +32,7 @@ extern auth_spa_options_block auth_spa_option_defaults;

/* The entry points for the mechanism */

-extern void auth_spa_init(auth_instance *);
+extern void auth_spa_init(driver_instance *);
extern int auth_spa_server(auth_instance *, uschar *);
extern int auth_spa_client(auth_instance *, void *, int, uschar *, int);

diff --git a/src/src/auths/tls.c b/src/src/auths/tls.c
index 85b9a6722..0bcb675f7 100644
--- a/src/src/auths/tls.c
+++ b/src/src/auths/tls.c
@@ -47,7 +47,7 @@ auth_tls_options_block auth_tls_option_defaults = {
#ifdef MACRO_PREDEF

/* Dummy values */
-void auth_tls_init(auth_instance *ablock) {}
+void auth_tls_init(driver_instance *ablock) {}
int auth_tls_server(auth_instance *ablock, uschar *data) {return 0;}
int auth_tls_client(auth_instance *ablock, void * sx,
int timeout, uschar *buffer, int buffsize) {return 0;}
@@ -66,9 +66,10 @@ enable consistency checks to be done, or anything else that needs
to be set up. */

 void
-auth_tls_init(auth_instance *ablock)
+auth_tls_init(driver_instance * a)
 {
-ablock->public_name = ablock->name;    /* needed for core code */
+auth_instance * ablock = (auth_instance *)a;
+ablock->public_name = a->name;    /* needed for core code */
 }



@@ -82,7 +83,7 @@ ablock->public_name = ablock->name;    /* needed for core code */
 int
 auth_tls_server(auth_instance *ablock, uschar *data)
 {
-auth_tls_options_block * ob = (auth_tls_options_block *)ablock->options_block;
+auth_tls_options_block * ob = ablock->drinst.options_block;


if (ob->server_param1)
auth_vars[expand_nmax++] = expand_string(ob->server_param1);
diff --git a/src/src/auths/tls.h b/src/src/auths/tls.h
index 472a3e260..6b9378687 100644
--- a/src/src/auths/tls.h
+++ b/src/src/auths/tls.h
@@ -25,7 +25,7 @@ extern auth_tls_options_block auth_tls_option_defaults;

/* The entry points for the mechanism */

-extern void auth_tls_init(auth_instance *);
+extern void auth_tls_init(driver_instance *);
extern int auth_tls_server(auth_instance *, uschar *);

/* End of tls.h */
diff --git a/src/src/drtables.c b/src/src/drtables.c
index 9872bc765..fff3af82f 100644
--- a/src/src/drtables.c
+++ b/src/src/drtables.c
@@ -67,12 +67,14 @@ auth_info auths_available[] = {

 #ifdef AUTH_CRAM_MD5
   {
-  .driver_name =    US"cram_md5",                              /* lookup name */
-  .options =        auth_cram_md5_options,
-  .options_count =    &auth_cram_md5_options_count,
-  .options_block =    &auth_cram_md5_option_defaults,
-  .options_len =    sizeof(auth_cram_md5_options_block),
-  .init =        auth_cram_md5_init,
+  .drinfo = {
+    .driver_name =    US"cram_md5",            /* lookup name */
+    .options =        auth_cram_md5_options,
+    .options_count =    &auth_cram_md5_options_count,
+    .options_block =    &auth_cram_md5_option_defaults,
+    .options_len =    sizeof(auth_cram_md5_options_block),
+    .init =        auth_cram_md5_init,
+    },
   .servercode =        auth_cram_md5_server,
   .clientcode =        auth_cram_md5_client,
   .version_report =    NULL,
@@ -82,12 +84,14 @@ auth_info auths_available[] = {


 #ifdef AUTH_CYRUS_SASL
   {
-  .driver_name =    US"cyrus_sasl",
-  .options =        auth_cyrus_sasl_options,
-  .options_count =    &auth_cyrus_sasl_options_count,
-  .options_block =    &auth_cyrus_sasl_option_defaults,
-  .options_len =    sizeof(auth_cyrus_sasl_options_block),
-  .init =        auth_cyrus_sasl_init,
+  .drinfo = {
+    .driver_name =    US"cyrus_sasl",
+    .options =        auth_cyrus_sasl_options,
+    .options_count =    &auth_cyrus_sasl_options_count,
+    .options_block =    &auth_cyrus_sasl_option_defaults,
+    .options_len =    sizeof(auth_cyrus_sasl_options_block),
+    .init =        auth_cyrus_sasl_init,
+    },
   .servercode =        auth_cyrus_sasl_server,
   .clientcode =        NULL,
   .version_report =    auth_cyrus_sasl_version_report,
@@ -97,12 +101,14 @@ auth_info auths_available[] = {


 #ifdef AUTH_DOVECOT
   {
-  .driver_name =    US"dovecot",
-  .options =        auth_dovecot_options,
-  .options_count =    &auth_dovecot_options_count,
-  .options_block =    &auth_dovecot_option_defaults,
-  .options_len =    sizeof(auth_dovecot_options_block),
-  .init =        auth_dovecot_init,
+  .drinfo = {
+    .driver_name =    US"dovecot",
+    .options =        auth_dovecot_options,
+    .options_count =    &auth_dovecot_options_count,
+    .options_block =    &auth_dovecot_option_defaults,
+    .options_len =    sizeof(auth_dovecot_options_block),
+    .init =        auth_dovecot_init,
+    },
   .servercode =        auth_dovecot_server,
   .clientcode =        NULL,
   .version_report =    NULL,
@@ -112,12 +118,14 @@ auth_info auths_available[] = {


 #ifdef AUTH_EXTERNAL
   {
-  .driver_name =    US"external",
-  .options =        auth_external_options,
-  .options_count =    &auth_external_options_count,
-  .options_block =    &auth_external_option_defaults,
-  .options_len =    sizeof(auth_external_options_block),
-  .init =        auth_external_init,
+  .drinfo = {
+    .driver_name =    US"external",
+    .options =        auth_external_options,
+    .options_count =    &auth_external_options_count,
+    .options_block =    &auth_external_option_defaults,
+    .options_len =    sizeof(auth_external_options_block),
+    .init =        auth_external_init,
+    },
   .servercode =        auth_external_server,
   .clientcode =        auth_external_client,
   .version_report =    NULL,
@@ -127,12 +135,14 @@ auth_info auths_available[] = {


 #ifdef AUTH_GSASL
   {
-  .driver_name =    US"gsasl",
-  .options =        auth_gsasl_options,
-  .options_count =    &auth_gsasl_options_count,
-  .options_block =    &auth_gsasl_option_defaults,
-  .options_len =    sizeof(auth_gsasl_options_block),
-  .init =        auth_gsasl_init,
+  .drinfo = {
+    .driver_name =    US"gsasl",
+    .options =        auth_gsasl_options,
+    .options_count =    &auth_gsasl_options_count,
+    .options_block =    &auth_gsasl_option_defaults,
+    .options_len =    sizeof(auth_gsasl_options_block),
+    .init =        auth_gsasl_init,
+    },
   .servercode =        auth_gsasl_server,
   .clientcode =        auth_gsasl_client,
   .version_report =    auth_gsasl_version_report,
@@ -142,12 +152,14 @@ auth_info auths_available[] = {


 #ifdef AUTH_HEIMDAL_GSSAPI
   {
-  .driver_name =    US"heimdal_gssapi",
-  .options =        auth_heimdal_gssapi_options,
-  .options_count =    &auth_heimdal_gssapi_options_count,
-  .options_block =    &auth_heimdal_gssapi_option_defaults,
-  .options_len =    sizeof(auth_heimdal_gssapi_options_block),
-  .init =        auth_heimdal_gssapi_init,
+  .drinfo = {
+    .driver_name =    US"heimdal_gssapi",
+    .options =        auth_heimdal_gssapi_options,
+    .options_count =    &auth_heimdal_gssapi_options_count,
+    .options_block =    &auth_heimdal_gssapi_option_defaults,
+    .options_len =    sizeof(auth_heimdal_gssapi_options_block),
+    .init =        auth_heimdal_gssapi_init,
+    },
   .servercode =        auth_heimdal_gssapi_server,
   .clientcode =        NULL,
   .version_report =    auth_heimdal_gssapi_version_report,
@@ -157,12 +169,14 @@ auth_info auths_available[] = {


 #ifdef AUTH_PLAINTEXT
   {
-  .driver_name =    US"plaintext",
-  .options =        auth_plaintext_options,
-  .options_count =    &auth_plaintext_options_count,
-  .options_block =    &auth_plaintext_option_defaults,
-  .options_len =    sizeof(auth_plaintext_options_block),
-  .init =        auth_plaintext_init,
+  .drinfo = {
+    .driver_name =    US"plaintext",
+    .options =        auth_plaintext_options,
+    .options_count =    &auth_plaintext_options_count,
+    .options_block =    &auth_plaintext_option_defaults,
+    .options_len =    sizeof(auth_plaintext_options_block),
+    .init =        auth_plaintext_init,
+    },
   .servercode =        auth_plaintext_server,
   .clientcode =        auth_plaintext_client,
   .version_report =    NULL,
@@ -172,12 +186,14 @@ auth_info auths_available[] = {


 #ifdef AUTH_SPA
   {
-  .driver_name =    US"spa",
-  .options =        auth_spa_options,
-  .options_count =    &auth_spa_options_count,
-  .options_block =    &auth_spa_option_defaults,
-  .options_len =    sizeof(auth_spa_options_block),
-  .init =        auth_spa_init,
+  .drinfo = {
+    .driver_name =    US"spa",
+    .options =        auth_spa_options,
+    .options_count =    &auth_spa_options_count,
+    .options_block =    &auth_spa_option_defaults,
+    .options_len =    sizeof(auth_spa_options_block),
+    .init =        auth_spa_init,
+    },
   .servercode =        auth_spa_server,
   .clientcode =        auth_spa_client,
   .version_report =    NULL,
@@ -187,12 +203,14 @@ auth_info auths_available[] = {


 #ifdef AUTH_TLS
   {
-  .driver_name =    US"tls",
-  .options =        auth_tls_options,
-  .options_count =    &auth_tls_options_count,
-  .options_block =    &auth_tls_option_defaults,
-  .options_len =    sizeof(auth_tls_options_block),
-  .init =        auth_tls_init,
+  .drinfo = {
+    .driver_name =    US"tls",
+    .options =        auth_tls_options,
+    .options_count =    &auth_tls_options_count,
+    .options_block =    &auth_tls_option_defaults,
+    .options_len =    sizeof(auth_tls_options_block),
+    .init =        auth_tls_init,
+    },
   .servercode =        auth_tls_server,
   .clientcode =        NULL,
   .version_report =    NULL,
@@ -200,7 +218,7 @@ auth_info auths_available[] = {
   },
 #endif


-  { .driver_name = US"" }        /* end marker */
+  { .drinfo = { .driver_name = US"" }}        /* end marker */
 };



@@ -372,7 +390,7 @@ router_info routers_available[] = {
   .ri_flags =        ri_notransport
   },
 #endif
-  { US"" }
+  { .drinfo = { .driver_name = US"" }}
 };



@@ -474,7 +492,7 @@ transport_info transports_available[] = {
   .local =        FALSE
   },
 #endif
-  { US"" }
+  { .drinfo = { .driver_name = US"" }}
 };


 #ifndef MACRO_PREDEF
@@ -483,8 +501,8 @@ gstring *
 auth_show_supported(gstring * g)
 {
 g = string_cat(g, US"Authenticators:");
-for (auth_info * ai = auths_available; ai->driver_name[0]; ai++)
-           g = string_fmt_append(g, " %s", ai->driver_name);
+for (auth_info * ai = auths_available; ai->drinfo.driver_name[0]; ai++)
+           g = string_fmt_append(g, " %s", ai->drinfo.driver_name);
 return string_cat(g, US"\n");
 }


diff --git a/src/src/exim.c b/src/src/exim.c
index 8a67b2aad..97a22f9d9 100644
--- a/src/src/exim.c
+++ b/src/src/exim.c
@@ -1377,9 +1377,9 @@ g = show_db_version(g);
show_string(is_stdout, g);
g = NULL;

-for (auth_info * authi = auths_available; *authi->driver_name != '\0'; ++authi)
-  if (authi->version_report)
-    g = (*authi->version_report)(g);
+for (auth_info * ai= auths_available; *ai->drinfo.driver_name != '\0'; ai++)
+  if (ai->version_report)
+    g = (*ai->version_report)(g);


   /* PCRE_PRERELEASE is either defined and empty or a bare sequence of
   characters; unless it's an ancient version of PCRE in which case it
diff --git a/src/src/globals.c b/src/src/globals.c
index 18ccc95a9..9efc389a6 100644
--- a/src/src/globals.c
+++ b/src/src/globals.c
@@ -22,7 +22,7 @@ optionlist optionlist_auths[] = {
   { "client_set_id", opt_stringptr | opt_public,
                  OPT_OFF(auth_instance, set_client_id) },
   { "driver",        opt_stringptr | opt_public,
-                 OPT_OFF(auth_instance, driver_name) },
+                 OPT_OFF(auth_instance, drinst.driver_name) },
   { "public_name",   opt_stringptr | opt_public,
                  OPT_OFF(auth_instance, public_name) },
   { "server_advertise_condition", opt_stringptr | opt_public,
@@ -632,11 +632,13 @@ uschar *authenticated_sender   = NULL;
 auth_instance  *auths          = NULL;
 uschar *auth_advertise_hosts   = US"*";
 auth_instance auth_defaults    = {
-    .next =        NULL,
-    .name =        NULL,
-    .info =        NULL,
-    .options_block =    NULL,
-    .driver_name =    NULL,
+    .drinst = {
+      .next =        NULL,
+      .name =        NULL,
+      .info =        NULL,
+      .options_block =    NULL,
+      .driver_name =    NULL,
+    },
     .advertise_condition = NULL,
     .client_condition =    NULL,
     .public_name =    NULL,
diff --git a/src/src/readconf.c b/src/src/readconf.c
index b79050f0c..b7df7be25 100644
--- a/src/src/readconf.c
+++ b/src/src/readconf.c
@@ -432,13 +432,16 @@ options_auths(void)
 {
 uschar buf[EXIM_DRIVERNAME_MAX];


-options_from_list(optionlist_auths, optionlist_auths_size, US"AUTHENTICATORS", NULL);
+options_from_list(optionlist_auths, optionlist_auths_size,
+ US"AUTHENTICATORS", NULL);

-for (struct auth_info * ai = auths_available; ai->driver_name[0]; ai++)
+for (struct auth_info * ai = auths_available; ai->drinfo.driver_name[0]; ai++)
   {
-  spf(buf, sizeof(buf), US"_DRIVER_AUTHENTICATOR_%T", ai->driver_name);
+  driver_info * di = &ai->drinfo;
+  spf(buf, sizeof(buf), US"_DRIVER_AUTHENTICATOR_%T", di->driver_name);
   builtin_macro_create(buf);
-  options_from_list(ai->options, (unsigned)*ai->options_count, US"AUTHENTICATOR", ai->driver_name);
+  options_from_list(di->options, (unsigned)*di->options_count,
+    US"AUTHENTICATOR", di->driver_name);


   if (ai->macros_create) (ai->macros_create)();
   }
@@ -4249,20 +4252,20 @@ readconf_driver_init(US"authenticator",
   optionlist_auths,                  /* generic options */
   optionlist_auths_size);


-for (auth_instance * au = auths; au; au = au->next)
+for (auth_instance * au = auths; au; au = au->drinst.next)
   {
   if (!au->public_name)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "no public name specified for "
-      "the %s authenticator", au->name);
+      "the %s authenticator", au->drinst.name);


-  for (auth_instance * bu = au->next; bu; bu = bu->next)
+  for (auth_instance * bu = au->drinst.next; bu; bu = bu->drinst.next)
     if (strcmpic(au->public_name, bu->public_name) == 0)
       if (  au->client && bu->client
      || au->server && bu->server)
         log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "two %s authenticators "
           "(%s and %s) have the same public name (%s)",
           au->client && bu->client ? US"client" : US"server",
-      au->name, bu->name, au->public_name);
+      au->drinst.name, bu->drinst.name, au->public_name);
 #ifndef DISABLE_PIPE_CONNECT
   nauths++;
 #endif
diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
index 521bc05aa..7c2b4a292 100644
--- a/src/src/smtp_in.c
+++ b/src/src/smtp_in.c
@@ -3408,9 +3408,9 @@ int rc;


/* Set up globals for error messages */

-authenticator_name = au->name;
-driver_srcfile = au->srcfile;
-driver_srcline = au->srcline;
+authenticator_name = au->drinst.name;
+driver_srcfile = au->drinst.srcfile;
+driver_srcline = au->drinst.srcline;

/* Run the checking code, passing the remainder of the command line as
data. Initials the $auth<n> variables as empty. Initialize $0 empty and set
@@ -3428,7 +3428,10 @@ for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
expand_nmax = 0;
expand_nlength[0] = 0; /* $0 contains nothing */

-rc = (au->info->servercode)(au, smtp_cmd_data);
+  {
+  auth_info * ai = au->drinst.info;
+  rc = (ai->servercode)(au, smtp_cmd_data);
+  }
 if (au->set_id) set_id = expand_string(au->set_id);
 expand_nmax = -1;        /* Reset numeric variables */
 for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;   /* Reset $auth<n> */
@@ -3457,7 +3460,7 @@ switch(rc)
     if (!au->set_id || set_id)    /* Complete success */
       {
       if (set_id) authenticated_id = string_copy_perm(set_id, TRUE);
-      sender_host_authenticated = au->name;
+      sender_host_authenticated = au->drinst.name;
       sender_host_auth_pubname  = au->public_name;
       authentication_failed = FALSE;
       authenticated_fail_id = NULL;   /* Impossible to already be set? */
@@ -3747,8 +3750,8 @@ while (done <= 0)
     {
     cmd_list[CL_TLAU].is_mail_cmd = FALSE;


-    for (auth_instance * au = auths; au; au = au->next)
-      if (strcmpic(US"tls", au->driver_name) == 0)
+    for (auth_instance * au = auths; au; au = au->drinst.next)
+      if (strcmpic(US"tls", au->drinst.driver_name) == 0)
     {
     GET_OPTION("acl_smtp_auth");
     if (  acl_smtp_auth
@@ -3768,7 +3771,7 @@ while (done <= 0)
 #ifndef DISABLE_EVENT
          {
           uschar * save_name = sender_host_authenticated, * logmsg;
-          sender_host_authenticated = au->name;
+          sender_host_authenticated = au->drinst.name;
           if ((logmsg = event_raise(event_action, US"auth:fail", s, NULL)))
         log_write(0, LOG_MAIN, "%s", logmsg);
           sender_host_authenticated = save_name;
@@ -3867,7 +3870,7 @@ while (done <= 0)
     auth_instance * au;
     uschar * smtp_resp, * errmsg;


-    for (au = auths; au; au = au->next)
+    for (au = auths; au; au = au->drinst.next)
       if (strcmpic(s, au->public_name) == 0 && au->server &&
           (au->advertised || f.allow_auth_unadvertised))
         break;
@@ -3882,7 +3885,7 @@ while (done <= 0)
         uschar * logmsg = NULL;
 #ifndef DISABLE_EVENT
          {uschar * save_name = sender_host_authenticated;
-          sender_host_authenticated = au->name;
+          sender_host_authenticated = au->drinst.name;
           logmsg = event_raise(event_action, US"auth:fail", smtp_resp, NULL);
           sender_host_authenticated = save_name;
          }
@@ -3891,7 +3894,7 @@ while (done <= 0)
           log_write(0, LOG_MAIN|LOG_REJECT, "%s", logmsg);
         else
           log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s",
-        au->name, host_and_ident(FALSE), errmsg);
+        au->drinst.name, host_and_ident(FALSE), errmsg);
         }
       }
     else
@@ -4215,17 +4218,17 @@ while (done <= 0)
        )
       {
       BOOL first = TRUE;
-      for (auth_instance * au = auths; au; au = au->next)
+      for (auth_instance * au = auths; au; au = au->drinst.next)
         {
         au->advertised = FALSE;
         if (au->server)
           {
           DEBUG(D_auth+D_expand) debug_printf_indent(
         "Evaluating advertise_condition for %s %s athenticator\n",
-        au->name, au->public_name);
+        au->drinst.name, au->public_name);
           if (  !au->advertise_condition
-         || expand_check_condition(au->advertise_condition, au->name,
-            US"authenticator")
+         || expand_check_condition(au->advertise_condition,
+                      au->drinst.name, US"authenticator")
          )
         {
         int saveptr;
@@ -4599,7 +4602,7 @@ while (done <= 0)
           if (authenticated_by == NULL ||
               authenticated_by->mail_auth_condition == NULL ||
               expand_check_condition(authenticated_by->mail_auth_condition,
-              authenticated_by->name, US"authenticator"))
+              authenticated_by->drinst.name, US"authenticator"))
             break;     /* Accept the AUTH */


           ignore_msg = US"server_mail_auth_condition failed";
diff --git a/src/src/structs.h b/src/src/structs.h
index 73163b6c2..ee312a44e 100644
--- a/src/src/structs.h
+++ b/src/src/structs.h
@@ -381,13 +381,7 @@ typedef struct router_info {
 mechanisms */


 typedef struct auth_instance {
-  struct auth_instance *next;
-  uschar *name;                   /* Exim instance name */
-  struct auth_info *info;         /* Pointer to driver info block */
-  void   *options_block;          /* Pointer to private options */
-  uschar *driver_name;            /* Must be first */
-  const uschar *srcfile;
-  int      srcline;
+  driver_instance drinst;


   uschar *advertise_condition;    /* Are we going to advertise this?*/
   uschar *client_condition;       /* Should the client try this? */
@@ -404,17 +398,11 @@ typedef struct auth_instance {



/* Structure for holding information about an authentication mechanism. The
-first six fields must match driver_info above. */
+first element must be a struct driver_info, to match routers and transports. */

 typedef struct auth_info {
-  uschar *driver_name;            /* e.g. "condition" */
-  optionlist *options;            /* Table of private options names */
-  int    *options_count;          /* -> Number of entries in table */
-  void   *options_block;          /* Points to default private block */
-  int     options_len;            /* Length of same in bytes */
-  void (*init)(                   /* initialization function */
-    struct auth_instance *);
-/****/
+  driver_info drinfo;
+
   int (*servercode)(              /* server function */
     auth_instance *,              /* the instance data */
     uschar *);                    /* rest of AUTH command */
diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
index f90ac16ba..25858d982 100644
--- a/src/src/transports/smtp.c
+++ b/src/src/transports/smtp.c
@@ -1033,14 +1033,15 @@ if (!regex_match_and_setup(regex_AUTH, sx->buffer, 0, -1)) return 0;
 expand_nmax = -1;                        /* reset */
 names = string_copyn(expand_nstring[1], expand_nlength[1]);


-for (au = auths, authnum = 0; au; au = au->next, authnum++) if (au->client)
-  {
-  const uschar * list = names;
-  uschar * s;
-  for (int sep = ' '; s = string_nextinlist(&list, &sep, NULL, 0); )
-    if (strcmpic(au->public_name, s) == 0)
-      { authbits |= BIT(authnum); break; }
-  }
+for (au = auths, authnum = 0; au; au = au->drinst.next, authnum++)
+  if (au->client)
+    {
+    const uschar * list = names;
+    uschar * s;
+    for (int sep = ' '; s = string_nextinlist(&list, &sep, NULL, 0); )
+      if (strcmpic(au->public_name, s) == 0)
+    { authbits |= BIT(authnum); break; }
+    }


DEBUG(D_transport)
debug_printf("server offers %s AUTH, methods '%s', usable-bitmap 0x%04x\n",
@@ -1514,16 +1515,20 @@ int rc;

/* Set up globals for error messages */

-authenticator_name = au->name;
-driver_srcfile = au->srcfile;
-driver_srcline = au->srcline;
+authenticator_name = au->drinst.name;
+driver_srcfile = au->drinst.srcfile;
+driver_srcline = au->drinst.srcline;

-sx->outblock.authenticating = TRUE;
-rc = (au->info->clientcode)(au, sx, ob->command_timeout,
-                sx->buffer, sizeof(sx->buffer));
-sx->outblock.authenticating = FALSE;
+  {
+  auth_info * ai = au->drinst.info;
+  sx->outblock.authenticating = TRUE;
+  rc = (ai->clientcode)(au, sx, ob->command_timeout,
+                  sx->buffer, sizeof(sx->buffer));
+  sx->outblock.authenticating = FALSE;
+  }
 driver_srcfile = authenticator_name = NULL; driver_srcline = 0;
-DEBUG(D_transport) debug_printf("%s authenticator yielded %s\n", au->name, rc_names[rc]);
+DEBUG(D_transport) debug_printf("%s authenticator yielded %s\n",
+  au->drinst.name, rc_names[rc]);


 /* A temporary authentication failure must hold up delivery to
 this host. After a permanent authentication failure, we carry on
@@ -1534,7 +1539,7 @@ switch(rc)
   {
   case OK:
     f.smtp_authenticated = TRUE;   /* stops the outer loop */
-    client_authenticator = au->name;
+    client_authenticator = au->drinst.name;
     if (au->set_client_id)
       client_authenticated_id = expand_string(au->set_client_id);
     break;
@@ -1554,16 +1559,16 @@ switch(rc)
 #ifndef DISABLE_EVENT
      {
       uschar * save_name = sender_host_authenticated;
-      sender_host_authenticated = au->name;
-      if ((logmsg = event_raise(sx->conn_args.tblock->event_action, US"auth:fail",
-                sx->buffer, NULL)))
+      sender_host_authenticated = au->drinst.name;
+      if ((logmsg = event_raise(sx->conn_args.tblock->event_action,
+                US"auth:fail", sx->buffer, NULL)))
     log_write(0, LOG_MAIN, "%s", logmsg);
       sender_host_authenticated = save_name;
      }
 #endif
     if (!logmsg)
       log_write(0, LOG_MAIN, "%s authenticator failed H=%s [%s] %s",
-    au->name, host->name, host->address, sx->buffer);
+    au->drinst.name, host->name, host->address, sx->buffer);
     break;
     }


@@ -1576,7 +1581,7 @@ switch(rc)
   case CANCELLED:
     if (*sx->buffer != 0)
       log_write(0, LOG_MAIN, "%s authenticator cancelled "
-    "authentication H=%s [%s] %s", au->name, host->name,
+    "authentication H=%s [%s] %s", au->drinst.name, host->name,
     host->address, sx->buffer);
     break;


@@ -1670,26 +1675,27 @@ if ( sx->esmtp

       for (bitnum = 0, au = auths;
        !f.smtp_authenticated && au && bitnum < 16;
-       bitnum++, au = au->next) if (authbits & BIT(bitnum))
-    {
-    if (  au->client_condition
-       && !expand_check_condition(au->client_condition, au->name,
-                   US"client authenticator"))
+       bitnum++, au = au->drinst.next)
+    if (authbits & BIT(bitnum))
       {
-      DEBUG(D_transport) debug_printf("skipping %s authenticator: %s\n",
-        au->name, "client_condition is false");
-      continue;
-      }
+      if (  au->client_condition
+         && !expand_check_condition(au->client_condition, au->drinst.name,
+             US"client authenticator"))
+        {
+        DEBUG(D_transport) debug_printf("skipping %s authenticator: %s\n",
+          au->drinst.name, "client_condition is false");
+        continue;
+        }


-    /* Found data for a listed mechanism. Call its client entry. Set
-    a flag in the outblock so that data is overwritten after sending so
-    that reflections don't show it. */
+      /* Found data for a listed mechanism. Call its client entry. Set
+      a flag in the outblock so that data is overwritten after sending so
+      that reflections don't show it. */


-    fail_reason = US"authentication attempt(s) failed";
+      fail_reason = US"authentication attempt(s) failed";


-    if ((rc = try_authenticator(sx, au)) != OK)
-      return rc;
-    }
+      if ((rc = try_authenticator(sx, au)) != OK)
+        return rc;
+      }
       }
     else
 #endif
@@ -1700,17 +1706,18 @@ if (  sx->esmtp
     If one is found, attempt to authenticate by calling its client function.
     */


-    for (auth_instance * au = auths; !f.smtp_authenticated && au; au = au->next)
+    for (auth_instance * au = auths; !f.smtp_authenticated && au;
+    au = au->drinst.next)
       {
-      uschar *p = names;
+      uschar * p = names;


       if (  !au->client
          || (   au->client_condition
-        &&  !expand_check_condition(au->client_condition, au->name,
+        &&  !expand_check_condition(au->client_condition, au->drinst.name,
            US"client authenticator")))
     {
     DEBUG(D_transport) debug_printf("skipping %s authenticator: %s\n",
-      au->name,
+      au->drinst.name,
       au->client ? "client_condition is false"
             : "not configured as a client");
     continue;


--
## subscription configuration (requires account):
## https://lists.exim.org/mailman3/postorius/lists/exim-cvs.lists.exim.org/
## unsubscribe (doesn't require an account):
## exim-cvs-unsubscribe@???
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/