[Exim] local_scan api version proposal

Top Page
Delete this message
Reply to this message
Author: Marc MERLIN
Date:  
To: Philip Hazel, Exim-users
New-Topics: Pedantic documentation addition Re: [Exim] local_scan api version proposal
Subject: [Exim] local_scan api version proposal
--
Philip,

I know that for now you're more confortable keeping the dynamically
loadable local_scan patch outside of the exim source, which is fine, but
working on sa-exim, I realized the following thing:
The patch has a binary compatiblity number, but it's not actually used
because it's not in the exim source, therefore never in sync.

I'll attach the whole patch (not that long) that I revised to support
a local_scan API version number which looks like x.y
x -> major number, only gets increased if API is changed in a non
     backwards compatible way
y -> minor number, gets increased every time you add features of the API



Goal:
if a module's x is lower than exim's then exim knows that the module
was build for an older, incompatible version of the API, and won't run
it.
If both x are equal but the module's y is higher than exim's, it means that
the module was built against an exim that had more features in the API
than the current running one, and that therefore it's not safe to run
the module either.
if both x are equal and the module's y is smaller or equal to exim's,
it means that exim has all the features that the module expected when it
was built (maybe more, but the module won't know or care)

In order to help us, could I ask you to include this patch in exim 4.15?

diff -urN exim-4.14/src/local_scan.h exim-4.14-dllocalscan/src/local_scan.h
--- exim-4.14/src/local_scan.h    Tue Mar 11 04:20:20 2003
+++ exim-4.14-dllocalscan/src/local_scan.h    Sat Mar 15 16:30:44 2003
@@ -71,6 +71,12 @@


#define SPOOL_DATA_START_OFFSET (MESSAGE_ID_LENGTH+3)

+/* local_scan() ABI version number for dynamic libraries
+ The major number is increased when the ABI is changed in a non
+ backward compatible way.
+ The minor number is increased each time a new feature is added (in a
+ way that doesn't break backward compatibility) -- Marc */
+#define LOCAL_SCAN_ABI_VERSION 1.0

/* Structure definitions that are documented as visible in the function. */


All you need to do is bump up the number as you update the local_scan
API.
If you could do this, we should be able to maintain the rest, separetely
from exim.

As for the full patch (still based on David Woodhouse's), I attached it.
Note that I wrote this after a long day of snowboarding, little sleep,
and no testing whatsoever except for a syntax check build :-)
If you call tell me that I did something stupid, I'd appreciate it.

I hope to be able to test the patch myself with the soon to be released
sa-exim 2.3 sometime next week.

Marc
--
"A mouse is a device used to point at the xterm you want to type in" - A.S.R.
Microsoft is to operating systems & security ....
                                      .... what McDonalds is to gourmet cooking
Home page: http://marc.merlins.org/   |   Finger marc_f@??? for PGP key
--
diff -urN exim-4.14/src/EDITME exim-4.14-dllocalscan/src/EDITME
--- exim-4.14/src/EDITME    Tue Mar 11 04:20:18 2003
+++ exim-4.14-dllocalscan/src/EDITME    Sat Mar 15 07:45:05 2003
@@ -388,6 +388,20 @@



 #------------------------------------------------------------------------------
+# On systems which support dynamic loading of shared libraries, Exim can
+# load a local_scan function specified in its config file instead of having
+# to be recompiled with the desired local_scan function. For a full
+# description of the API to this function, see the Exim specification.
+
+DLOPEN_LOCAL_SCAN=yes
+
+# If you set DLOPEN_LOCAL_SCAN, then you need to include -rdynamic in the
+# linker flags.  Without it, the loaded .so won't be able to access any
+# functions from exim.
+
+LFLAGS=-rdynamic
+
+#------------------------------------------------------------------------------
 # The default distribution of Exim contains only the plain text form of the
 # documentation. Other forms are available separately. If you want to install
 # the documentation in "info" format, first fetch the Texinfo documentation
diff -urN exim-4.14/src/config.h.defaults exim-4.14-dllocalscan/src/config.h.defaults
--- exim-4.14/src/config.h.defaults    Tue Mar 11 04:20:19 2003
+++ exim-4.14-dllocalscan/src/config.h.defaults    Sat Mar 15 07:45:05 2003
@@ -17,6 +17,8 @@
 #define AUTH_PLAINTEXT
 #define AUTH_SPA


+#define DLOPEN_LOCAL_SCAN
+
#define BIN_DIRECTORY

 #define CONFIGURE_FILE
diff -urN exim-4.14/src/globals.c exim-4.14-dllocalscan/src/globals.c
--- exim-4.14/src/globals.c    Tue Mar 11 04:20:20 2003
+++ exim-4.14-dllocalscan/src/globals.c    Sat Mar 15 07:45:05 2003
@@ -103,6 +103,9 @@
 uschar *tls_verify_hosts       = NULL;
 #endif


+#ifdef DLOPEN_LOCAL_SCAN
+uschar *local_scan_path        = NULL;
+#endif


 /* Input-reading functions for messages, so we can use special ones for
 incoming TCP/IP. The defaults use stdin. We never need these for any
diff -urN exim-4.14/src/globals.h exim-4.14-dllocalscan/src/globals.h
--- exim-4.14/src/globals.h    Tue Mar 11 04:20:20 2003
+++ exim-4.14-dllocalscan/src/globals.h    Sat Mar 15 07:45:05 2003
@@ -67,6 +67,9 @@
 extern uschar *tls_verify_hosts;       /* Mandatory client verification */
 #endif


+#ifdef DLOPEN_LOCAL_SCAN
+extern uschar *local_scan_path;        /* Path to local_scan() library */
+#endif


 /* Input-reading functions for messages, so we can use special ones for
 incoming TCP/IP. */
diff -urN exim-4.14/src/local_scan.c exim-4.14-dllocalscan/src/local_scan.c
--- exim-4.14/src/local_scan.c    Tue Mar 11 04:20:20 2003
+++ exim-4.14-dllocalscan/src/local_scan.c    Sat Mar 15 21:58:40 2003
@@ -5,60 +5,116 @@
 /* Copyright (c) University of Cambridge 1995 - 2003 */
 /* See the file NOTICE for conditions of use and distribution. */


+#include "exim.h"

-/******************************************************************************
-This file contains a template local_scan() function that just returns ACCEPT.
-If you want to implement your own version, you should copy this file to, say
-Local/local_scan.c, and edit the copy. To use your version instead of the
-default, you must set
-
-LOCAL_SCAN_SOURCE=Local/local_scan.c
-
-in your Local/Makefile. This makes it easy to copy your version for use with
-subsequent Exim releases.
-
-For a full description of the API to this function, see the Exim specification.
-******************************************************************************/
-
-
-/* This is the only Exim header that you should include. The effect of
-including any other Exim header is not defined, and may change from release to
-release. Use only the documented interface! */
-
-#include "local_scan.h"
-
-
-/* This is a "do-nothing" version of a local_scan() function. The arguments
-are:
-
-  fd             The file descriptor of the open -D file, which contains the
-                   body of the message. The file is open for reading and
-                   writing, but modifying it is dangerous and not recommended.
-
-  return_text    A pointer to an unsigned char* variable which you can set in
-                   order to return a text string. It is initialized to NULL.
-
-The return values of this function are:
-
-  LOCAL_SCAN_ACCEPT
-                 The message is to be accepted. The return_text argument is
-                   saved in $local_scan_data.
-
-  LOCAL_SCAN_REJECT
-                 The message is to be rejected. The returned text is used
-                   in the rejection message.
-
-  LOCAL_SCAN_TEMPREJECT
-                 This specifies a temporary rejection. The returned text
-                   is used in the rejection message.
-*/
+#ifdef DLOPEN_LOCAL_SCAN
+#include <dlfcn.h>
+static int (*local_scan_fn)(int fd, uschar **return_text) = NULL;
+static int load_local_scan_library(void);
+#endif


 int
 local_scan(int fd, uschar **return_text)
 {
 fd = fd;                      /* Keep picky compilers happy */
 return_text = return_text;
-return LOCAL_SCAN_ACCEPT;
+#ifdef DLOPEN_LOCAL_SCAN
+/* local_scan_path is defined AND not the empty string */
+if (local_scan_path && *local_scan_path)
+  {
+  if (!local_scan_fn)
+    {
+    if (!load_local_scan_library())
+      {
+        char *base_msg , *error_msg , *final_msg ;
+        int final_length = -1 ;
+
+        base_msg=US"Local configuration error - local_scan() library failure\n";
+        error_msg = dlerror() ;
+
+        final_length = strlen(base_msg) + strlen(error_msg) + 1 ;
+        final_msg = (char*)malloc( final_length*sizeof(char) ) ;
+        *final_msg = '\0' ;
+
+        strcat( final_msg , base_msg ) ;
+        strcat( final_msg , error_msg ) ;
+
+        *return_text = final_msg ;
+      return LOCAL_SCAN_TEMPREJECT;
+      }
+    }
+    return local_scan_fn(fd, return_text);
+  }
+else
+#endif
+  return LOCAL_SCAN_ACCEPT;
+}
+
+#ifdef DLOPEN_LOCAL_SCAN
+
+static int load_local_scan_library(void)
+{
+/* No point in keeping local_scan_lib since we'll never dlclose() anyway */
+void *local_scan_lib = NULL;
+float (*local_scan_version_fn)(void);
+float vers;
+
+local_scan_lib = dlopen(local_scan_path, RTLD_NOW);
+if (!local_scan_lib)
+  {
+  log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() library open failed - "
+    "message temporarily rejected");
+  return FALSE;
+  }
+
+local_scan_version_fn = dlsym(local_scan_lib, "local_scan_version");
+if (!local_scan_version_fn)
+  {
+  dlclose(local_scan_lib);
+  log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() library doesn't contain "
+    "local_scan_version() function - message temporarily rejected");
+  return FALSE;
+  }
+
+vers = local_scan_version_fn();
+/* The major number is increased when the ABI is changed in a non
+   backward compatible way.
+   The minor number is increased each time a new feature is added (in a
+   way that doesn't break backward compatibility) -- Marc */
+
+if (( int)vers != (int)LOCAL_SCAN_ABI_VERSION)
+  {
+  dlclose(local_scan_lib);
+  local_scan_lib = NULL;
+  log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() has an incompatible major"
+    "version number, you need to recompile your module for this version"
+    "of exim (The module was compiled for version %.1f and this exim provides"
+    "ABI version %.1f)", vers, LOCAL_SCAN_ABI_VERSION);
+  return FALSE;
+  }
+else if ((vers - (int) vers) > (LOCAL_SCAN_ABI_VERSION - (int) LOCAL_SCAN_ABI_VERSION))
+  {
+  dlclose(local_scan_lib);
+  local_scan_lib = NULL;
+  log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() has an incompatible minor"
+    "version number, you need to recompile your module for this version"
+    "of exim (The module requires at least version %.1f but this exim only"
+    "provides ABI version %.1f)", vers, LOCAL_SCAN_ABI_VERSION);
+  return FALSE;
+  }
+
+local_scan_fn = dlsym(local_scan_lib, "local_scan");
+if (!local_scan_fn)
+  {
+  dlclose(local_scan_lib);
+  log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() library doesn't contain "
+    "local_scan() function - message temporarily rejected");
+  return FALSE;
+  }
+
+return TRUE;
 }
+
+#endif /* DLOPEN_LOCAL_SCAN */


 /* End of local_scan.c */
diff -urN exim-4.14/src/local_scan.h exim-4.14-dllocalscan/src/local_scan.h
--- exim-4.14/src/local_scan.h    Tue Mar 11 04:20:20 2003
+++ exim-4.14-dllocalscan/src/local_scan.h    Sat Mar 15 16:30:44 2003
@@ -71,6 +71,12 @@


#define SPOOL_DATA_START_OFFSET (MESSAGE_ID_LENGTH+3)

+/* local_scan() ABI version number for dynamic libraries
+ The major number is increased when the ABI is changed in a non
+ backward compatible way.
+ The minor number is increased each time a new feature is added (in a
+ way that doesn't break backward compatibility) -- Marc */
+#define LOCAL_SCAN_ABI_VERSION 1.0

/* Structure definitions that are documented as visible in the function. */

diff -urN exim-4.14/src/readconf.c exim-4.14-dllocalscan/src/readconf.c
--- exim-4.14/src/readconf.c    Tue Mar 11 04:20:22 2003
+++ exim-4.14-dllocalscan/src/readconf.c    Sat Mar 15 07:45:05 2003
@@ -182,6 +182,9 @@
   { "local_from_prefix",        opt_stringptr,   &local_from_prefix },
   { "local_from_suffix",        opt_stringptr,   &local_from_suffix },
   { "local_interfaces",         opt_stringptr,   &local_interfaces },
+#ifdef DLOPEN_LOCAL_SCAN
+  { "local_scan_path",          opt_stringptr,   &local_scan_path },
+#endif
   { "local_scan_timeout",       opt_time,        &local_scan_timeout },
   { "local_sender_retain",      opt_bool,        &local_sender_retain },
   { "localhost_number",         opt_stringptr,   &host_number_string },
--