[Exim] [patch] dynamically loadable lookups

Top Page
Delete this message
Reply to this message
Author: Steve Haslam
Date:  
To: exim-users
Subject: [Exim] [patch] dynamically loadable lookups
OK, this is an initial patch to Exim 3.12 to allow lookups to be moved
into dynamic libraries. As I mentioned previously, this is aimed at
package maintainers who want to provide an Exim package without
dependencies on LDAP/SQL libraries.

Comments:
. The build system goes through contortions to Do The Right Thing. I want
to look at this when I have more time (ha ha).
. Adding the dynamic types onto the lookup table is something of a hack.
This probably ought to be redone (perhaps making the tables justs lists of
pointers to the lookup_info structs, which is handier for .so modules)
. The impact on people not using dynamic lookups should be low, but someone
might like to look into how it can be improved.
. Maybe where objects store which lookup type they've used, they could
just keep a lookup_info pointer instead of an array index? this would
allow more freedom in how the table is stored...
. This patch has only been tested on Linux, but I should imagine it will
work on any OS where dlopen(), dlsym() and dlclose() work in the same
way. I added the following to Local/Makefile:

LOOKUP_LDAP_DYNAMIC=yes
SUPPORT_DLOOKUPS=yes
LOOKUPDLS=ldap.so
DLFLAGS=-fPIC -shared
LOOKUP_MODULE_DIR=/usr/home/shaslam/exim/exim-3.12/build-Linux-i386/lookups
EXTRALIBS=-ldl

. Only the directory listed in LOOKUP_MODULE_DIR will be searched for
modules. This is harsh for testing (you have to reconfigure before
installing, yuk), but I'm not sure of how to balance convenience and
security here (you don't want a setuid application loading any old
.so files)

Currently, I've modified only the LDAP lookup type to by dynamically
loadable.

SRH
-- 
Steve Haslam, Production Engineer, Excite UK     steve.haslam@???
                               i sit and stare at the gun pointed at my head
                                       and think about all the possibilities

diff -r -P -u -x *~ upstream/exim-3.12/src/config.h.defaults exim-3.12/src/config.h.defaults
--- upstream/exim-3.12/src/config.h.defaults    Wed Dec  8 09:57:04 1999
+++ exim-3.12/src/config.h.defaults    Wed Jan 12 20:39:10 2000
@@ -49,6 +49,7 @@
 #define LOOKUP_DBM
 #define LOOKUP_DNSDB
 #define LOOKUP_LDAP
+#define LOOKUP_LDAP_DYNAMIC
 #define LOOKUP_LSEARCH
 #define LOOKUP_MYSQL
 #define LOOKUP_NIS
@@ -82,6 +83,8 @@
 #define SUPPORT_MOVE_FROZEN_MESSAGES
 #define SUPPORT_PAM
 #define SUPPORT_TRANSLATE_IP_ADDRESS
+#define SUPPORT_DLOOKUPS
+#define LOOKUP_MODULE_DIR


 #define TRANSPORT_APPENDFILE
 #define TRANSPORT_AUTOREPLY
diff -r -P -u -x *~ upstream/exim-3.12/src/drtables.c exim-3.12/src/drtables.c
--- upstream/exim-3.12/src/drtables.c    Wed Dec  8 09:57:05 1999
+++ exim-3.12/src/drtables.c    Wed Jan 12 20:47:07 2000
@@ -8,6 +8,7 @@


#include "exim.h"

+#include <dlfcn.h>

 /* This module contains tables that define the lookup methods and drivers
 that are actually included in the binary. Its contents are controlled by
@@ -80,8 +81,9 @@
                              (for single-key style only)
 */


+lookup_info *lookup_list;

-lookup_info lookup_list[] = {
+static lookup_info static_lookup_list[] = {

/* cdb lookup in single file */

@@ -153,40 +155,6 @@
#endif
},

-/* LDAP lookup */
-
-  {
-  "ldap",                        /* lookup name */
-  lookup_querystyle,             /* query-style lookup */
-#ifdef LOOKUP_LDAP
-  eldap_open,                    /* open function */
-  NULL,                          /* check function */
-  eldap_find,                    /* find function */
-  NULL,                          /* no close function */
-  eldap_tidy,                    /* tidy function */
-  eldap_quote                    /* quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
-#endif
-  },
-
-/* LDAP lookup, allowing multiple values to be returned */
-
-  {
-  "ldapm",                       /* lookup name */
-  lookup_querystyle,             /* query-style lookup */
-#ifdef LOOKUP_LDAP
-  eldap_open,       /* sic */    /* open function */
-  NULL,                          /* check function */
-  eldapm_find,                   /* find function */
-  NULL,                          /* no close function */
-  eldap_tidy,       /* sic */    /* tidy function */
-  eldap_quote       /* sic */    /* quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
-#endif
-  },
-
 /* Linear search of single file */


{
@@ -600,5 +568,131 @@
};

 #endif  /* TEST_EXPAND */
+
+typedef struct lookupmodulestr 
+{
+  void *dl;
+  struct lookup_module_info *info;
+  struct lookupmodulestr *next;
+};
+
+static struct lookupmodulestr *lookupmodules;
+
+static void addlookupmodule(void *dl, struct lookup_module_info *info)
+{
+  struct lookupmodulestr *p = store_malloc(sizeof(struct lookupmodulestr));
+  p->dl = dl;
+  p->info = info;
+  if (lookupmodules)
+    lookupmodules->next = p;
+  lookupmodules = p;
+}
+
+void init_lookup_list(void)
+{
+  DIR *dd;
+  struct dirent *ent;
+  pcre *regex_islookupmod = regex_must_compile("\\.so$", FALSE, TRUE);
+  int countmodules = 0;
+  int moduleerrors = 0;
+  
+  /* Our failsafe is to stick to the lookups that were compiled
+     in statically */
+  lookup_list = static_lookup_list;
+
+#if defined(LOOKUP_LDAP) && !defined(LOOKUP_LDAP_DYNAMIC)
+  addlookupmodule(NULL, &ldap_lookup_module_info);
+#endif
+
+  dd = opendir(LOOKUP_MODULE_DIR);
+  if (dd == NULL) {
+    DEBUG(5) debug_printf("Couldn't open %s: not loading lookup modules\n", LOOKUP_MODULE_DIR);
+  }
+  else {
+    DEBUG(9) debug_printf("Loading lookup modules from %s\n", LOOKUP_MODULE_DIR);
+    while ((ent = readdir(dd)) != NULL) {
+      char *name = ent->d_name;
+      int len = (int)strlen(name);
+      if (pcre_exec(regex_islookupmod, NULL, name, len, 0, PCRE_EOPT, NULL, 0) >= 0) {
+    int pathnamelen = len + (int)strlen(LOOKUP_MODULE_DIR) + 2;
+    void *dl;
+    struct lookup_module_info *info;
+    char *errormsg;
+      
+    /* SRH: am I being paranoid here or what? */
+    if (pathnamelen > big_buffer_size) {
+      fprintf(stderr, "%s/%s: name too long\n", LOOKUP_MODULE_DIR, name);
+      continue;
+    }
+    /* SRH: snprintf here? */
+    sprintf(big_buffer, "%s/%s", LOOKUP_MODULE_DIR, name);
+
+    dl = dlopen(big_buffer, RTLD_LAZY);
+    if (dl == NULL) {
+      fprintf(stderr, "Error loading %s: %s\n", name, dlerror());
+      moduleerrors++;
+      continue;
+    }
+    info = (struct lookup_module_info*) dlsym(dl, "_lookup_module_info");
+    if ((errormsg = dlerror()) != NULL) {
+      fprintf(stderr, "%s does not appear to be a lookup module (%s)\n", name, errormsg);
+      dlclose(dl);
+      moduleerrors++;
+      continue;
+    }
+    if (info->magic != LOOKUP_MODULE_INFO_MAGIC) {
+      fprintf(stderr, "Lookup module %s is not compatible with this version of Exim\n", name);
+      dlclose(dl);
+      moduleerrors++;
+      continue;
+    }
+    addlookupmodule(dl, info);
+    DEBUG(9) debug_printf("Loaded \"%s\" (%d lookup types)\n", name, info->lookupcount);
+    countmodules++;
+      }
+    }
+    closedir(dd);
+  }
+  
+  DEBUG(9) debug_printf("Loaded %d lookup modules\n", countmodules);
+  if (lookupmodules) {
+    struct lookupmodulestr *p;
+    int staticlookups = sizeof(static_lookup_list)/sizeof(static_lookup_list[0]) - 1;
+    int i;
+    int dynamiclookups = 0;
+    
+    for (p = lookupmodules; p; p = p->next) {
+      dynamiclookups += p->info->lookupcount;
+    }
+    
+    DEBUG(9) debug_printf("Appending %d dynamic lookups to %d static lookups\n", dynamiclookups, staticlookups);
+    
+    lookup_list = store_malloc(sizeof(struct lookup_info) * dynamiclookups + sizeof(static_lookup_list));
+
+    /* static lookups get low numbers */
+    memcpy(lookup_list, static_lookup_list, sizeof(static_lookup_list));
+    i = staticlookups;
+
+    /* add dynamic lookups */
+    for (p = lookupmodules; p; p=p->next) {
+      int j;
+      for (j = 0; j < p->info->lookupcount; j++) {
+    lookup_list[i++] = *p->info->lookups[j];
+      }
+    }
+    
+    /* copy terminator */
+    memcpy(lookup_list + i, static_lookup_list + staticlookups, sizeof(struct lookup_info));
+
+    DEBUG(4) {
+      lookup_info *li;
+      debug_printf("Lookups:", i);
+      for (li = lookup_list, i = 0; li->name[0] != 0; i++, li++) {
+    debug_printf(" %s=%d", li->name, i);
+      }
+      debug_printf("\n");
+    }
+  }
+}


 /* End of drtables.c */
diff -r -P -u -x *~ upstream/exim-3.12/src/exim.c exim-3.12/src/exim.c
--- upstream/exim-3.12/src/exim.c    Wed Dec  8 09:57:05 1999
+++ exim-3.12/src/exim.c    Wed Jan 12 15:14:59 2000
@@ -1769,6 +1769,9 @@
   }
 #endif /* EXIM_PERL */


+/* Initialise lookup_list */
+ init_lookup_list();
+
 /* Log the arguments of the call if the configuration file said so. This is
 a debugging feature for finding out what arguments certain MUAs actually use.
 Don't attempt it if logging is disabled, or if listing variables or if
diff -r -P -u -x *~ upstream/exim-3.12/src/globals.h exim-3.12/src/globals.h
--- upstream/exim-3.12/src/globals.h    Wed Dec  8 09:57:05 1999
+++ exim-3.12/src/globals.h    Wed Jan 12 15:12:05 2000
@@ -258,7 +258,7 @@
 extern FILE  *log_stderr;             /* Copy of stderr for log use, or NULL */
 extern BOOL   log_subject;            /* As it says */
 extern char  *lookup_key;             /* For query expansion */
-extern lookup_info lookup_list[];     /* Vector of available lookups */
+extern lookup_info *lookup_list;      /* Array of available lookups */
 extern int    lookup_open_max;        /* Max lookup files to cache */
 extern char  *lookup_value;           /* Value looked up from file */


diff -r -P -u -x *~ upstream/exim-3.12/src/lookupapi.h exim-3.12/src/lookupapi.h
--- upstream/exim-3.12/src/lookupapi.h    Thu Jan  1 01:00:00 1970
+++ exim-3.12/src/lookupapi.h    Wed Jan 12 16:59:56 2000
@@ -0,0 +1,50 @@
+/* This header contains sufficient source for building dynamic
+   lookup libraries for Exim */
+
+#ifndef __exim_lookupapi_h
+#define __exim_lookupapi_h
+
+#ifndef BOOL
+#define BOOL int
+#endif
+
+/* Structure for holding information about a lookup type. */
+
+typedef struct lookup_info {
+  char *name;                    /* e.g. "lsearch" */
+  int type;                      /* query/singlekey/abs-file */
+  void *(*open)(                 /* open function */
+    char *,                      /* file name for those that have one */
+    char **);                    /* for error message */
+  BOOL (*check)(                 /* file checking function */
+    void *,                      /* handle */
+    char *,                      /* file name */
+    int,                         /* modemask for file checking */
+    int *,                       /* owners for file checking */
+    int *,                       /* owngroups for file checking */
+    char **);                    /* for error messages */
+  int (*find)(                   /* find function */
+    void *,                      /* handle */
+    char *,                      /* file name or NULL */
+    char *,                      /* key or query */
+    int,                         /* length of key or query */
+    char **,                     /* for returning answer */
+    char **);                    /* for error message */
+  void (*close)(                 /* close function */
+    void *);                     /* handle */
+  void (*tidy)(void);            /* tidy function */
+  char *(*quote)(                /* quoting function */
+    char *);                     /* string to quote */
+} lookup_info;
+
+/* This structure is the interface with a dynamically-loaded lookup module */
+/* Bump the magic number if it or struct lookup_info changes */
+#define LOOKUP_MODULE_INFO_MAGIC 0x45590001U
+typedef struct lookup_module_info 
+{
+  unsigned int magic;
+  lookup_info **lookups;
+  int lookupcount;
+} lookup_module_info;
+
+#endif /* !defined(__lookupapi_h) */
diff -r -P -u -x *~ upstream/exim-3.12/src/lookups/Makefile exim-3.12/src/lookups/Makefile
--- upstream/exim-3.12/src/lookups/Makefile    Wed Dec  8 09:57:06 1999
+++ exim-3.12/src/lookups/Makefile    Wed Jan 12 16:24:00 2000
@@ -5,14 +5,19 @@


OBJ = cdb.o dbmdb.o dnsdb.o ldap.o lsearch.o mysql.o nis.o nisplus.o testdb.o

+all: lookups.a $(LOOKUPDLS)
+
 lookups.a:       $(OBJ)
          /bin/rm -f lookups.a
          $(AR) lookups.a $(OBJ)
          $(RANLIB) $@
          /bin/rm -rf ../drtables.o


-.SUFFIXES:       .o .c
+.SUFFIXES:       .o .c .so
 .c.o:;           $(CC) -c $(CFLAGS) $(INCLUDE) $*.c
+
+ldap.so: ldap.c
+    $(CC) -DDYNLOOKUP $(CFLAGS) $(INCLUDE) $(DLFLAGS) $*.c -lldap -llber -lc -o $@


 cdb.o:           $(HDRS) cdb.c       cdb.h
 dbmdb.o:         $(HDRS) dbmdb.c     dbmdb.h
diff -r -P -u -x *~ upstream/exim-3.12/src/lookups/dltest.c exim-3.12/src/lookups/dltest.c
--- upstream/exim-3.12/src/lookups/dltest.c    Thu Jan  1 01:00:00 1970
+++ exim-3.12/src/lookups/dltest.c    Wed Jan 12 16:26:27 2000
@@ -0,0 +1,12 @@
+#include "../exim.h"
+
+struct lookup_info _dltest_lookup_info =
+{ "dltest", lookup_querystyle,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL 
+};
+
diff -r -P -u -x *~ upstream/exim-3.12/src/lookups/ldap.c exim-3.12/src/lookups/ldap.c
--- upstream/exim-3.12/src/lookups/ldap.c    Wed Dec  8 09:57:06 1999
+++ exim-3.12/src/lookups/ldap.c    Wed Jan 12 20:43:47 2000
@@ -12,7 +12,6 @@
 #include "../exim.h"
 #include "ldap.h"


-
/* We can't just compile this code and allow the library mechanism to omit the
functions if they are not wanted, because we need to have the LDAP headers
available for compiling. Therefore, compile these functions only if LOOKUP_LDAP
@@ -21,10 +20,52 @@
stop picky compilers complaining that it is unused, and put in a dummy argument
to stop even pickier compilers complaining about infinite loops. */

-#ifndef LOOKUP_LDAP
+#if !defined(LOOKUP_LDAP) || (defined(LOOKUP_LDAP_DYNAMIC) && !defined(DYNLOOKUP))
static void dummy(int x) { dummy(x-1); }
#else

+/* These are the lookup_info blocks for this driver */
+/* This driver provides two lookup types */
+
+/* LDAP lookup */
+lookup_info ldap_lookup_info = {
+  "ldap",                        /* lookup name */
+  lookup_querystyle,             /* query-style lookup */
+#ifdef LOOKUP_LDAP
+  eldap_open,                    /* open function */
+  NULL,                          /* check function */
+  eldap_find,                    /* find function */
+  NULL,                          /* no close function */
+  eldap_tidy,                    /* tidy function */
+  eldap_quote                    /* quoting function */
+#else
+  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
+#endif
+};
+
+
+/* LDAP lookup, allowing multiple values to be returned */
+lookup_info ldapm_lookup_info = {
+  "ldapm",                       /* lookup name */
+  lookup_querystyle,             /* query-style lookup */
+#ifdef LOOKUP_LDAP
+  eldap_open,       /* sic */    /* open function */
+  NULL,                          /* check function */
+  eldapm_find,                   /* find function */
+  NULL,                          /* no close function */
+  eldap_tidy,       /* sic */    /* tidy function */
+  eldap_quote       /* sic */    /* quoting function */
+#else
+  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
+#endif
+};
+
+#ifdef DYNLOOKUP
+#define ldap_lookup_module_info _lookup_module_info
+#endif
+
+static lookup_info *_lookup_list[] = { &ldap_lookup_info, &ldapm_lookup_info };
+lookup_module_info ldap_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 2 };


/* Include LDAP headers */

diff -r -P -u -x *~ upstream/exim-3.12/src/lookups/ldap.h exim-3.12/src/lookups/ldap.h
--- upstream/exim-3.12/src/lookups/ldap.h    Wed Dec  8 09:57:06 1999
+++ exim-3.12/src/lookups/ldap.h    Wed Jan 12 16:45:51 2000
@@ -7,6 +7,8 @@


/* Header for the ldap lookup */

+extern lookup_module_info ldap_lookup_module_info;
+
 extern void *eldap_open(char *, char **);
 extern int   eldap_find(void *, char *, char *, int, char **, char **);
 extern void  eldap_tidy(void);
diff -r -P -u -x *~ upstream/exim-3.12/src/structs.h exim-3.12/src/structs.h
--- upstream/exim-3.12/src/structs.h    Wed Dec  8 09:57:09 1999
+++ exim-3.12/src/structs.h    Wed Jan 12 16:59:01 2000
@@ -327,35 +327,7 @@
 } router_info;



-/* Structure for holding information about a lookup type. */
-
-typedef struct lookup_info {
-  char *name;                    /* e.g. "lsearch" */
-  int type;                      /* query/singlekey/abs-file */
-  void *(*open)(                 /* open function */
-    char *,                      /* file name for those that have one */
-    char **);                    /* for error message */
-  BOOL (*check)(                 /* file checking function */
-    void *,                      /* handle */
-    char *,                      /* file name */
-    int,                         /* modemask for file checking */
-    int *,                       /* owners for file checking */
-    int *,                       /* owngroups for file checking */
-    char **);                    /* for error messages */
-  int (*find)(                   /* find function */
-    void *,                      /* handle */
-    char *,                      /* file name or NULL */
-    char *,                      /* key or query */
-    int,                         /* length of key or query */
-    char **,                     /* for returning answer */
-    char **);                    /* for error message */
-  void (*close)(                 /* close function */
-    void *);                     /* handle */
-  void (*tidy)(void);            /* tidy function */
-  char *(*quote)(                /* quoting function */
-    char *);                     /* string to quote */
-} lookup_info;
-
+#include "lookupapi.h"


 /* Structure for holding information about the configured authentication
 mechanisms */
diff -r -P -u -x *~ upstream/exim-3.12/scripts/MakeLinks exim-3.12/scripts/MakeLinks
--- upstream/exim-3.12/scripts/MakeLinks    Wed Dec  8 09:57:03 1999
+++ exim-3.12/scripts/MakeLinks    Wed Jan 12 17:00:53 2000
@@ -157,6 +157,7 @@
 ln -s ../src/globals.h         globals.h
 ln -s ../src/macros.h          macros.h
 ln -s ../src/structs.h         structs.h
+ln -s ../src/lookupapi.h       lookupapi.h


 ln -s ../src/accept.c          accept.c
 ln -s ../src/buildconfig.c     buildconfig.c
diff -r -P -u -x *~ upstream/exim-3.12/OS/Makefile-Base exim-3.12/OS/Makefile-Base
--- upstream/exim-3.12/OS/Makefile-Base    Wed Dec  8 09:57:01 1999
+++ exim-3.12/OS/Makefile-Base    Wed Jan 12 16:22:37 2000
@@ -429,6 +429,7 @@
 buildlookups:
      @cd lookups; $(MAKE) SHELL=$(SHELL) AR="$(AR)" $(MFLAGS) CC="$(CC)" CFLAGS="$(CFLAGS)" \
        RANLIB="$(RANLIB)" HDRS="$(PHDRS)" \
+           LOOKUPDLS="$(LOOKUPDLS)" DLFLAGS="$(DLFLAGS)" \
        INCLUDE="$(INCLUDE) $(IPV6_INCLUDE) $(LOOKUP_INCLUDE)"; \
      echo " "


--- upstream/exim-3.12/Makefile    Wed Dec  8 09:57:01 1999
+++ exim-3.12/Makefile    Wed Jan 12 20:45:48 2000
@@ -75,10 +75,10 @@
     cd build-$(buildname); \
     /bin/rm -f *.o lookups/*.o lookups/*.a directors/*.o directors/*.a \
     routers/*.o routers/*.a transports/*.o transports/*.a \
-    libident/*.o libident/*.a pcre/*.o pcre/*.a
+    libident/*.o libident/*.a pcre/*.o pcre/*.a lookups/*.so


 clean_exim:; cd build-$(buildname); \
      /bin/rm -f *.o lookups/*.o lookups/*.a directors/*.o directors/*.a \
-    transports/*.o transports/*.a routers/*.o routers/*.a
+    transports/*.o transports/*.a routers/*.o routers/*.a lookups/*.so


# End of top-level makefile