Update the GPL boilerplate with the new address of the FSF.
[freeradius.git] / src / main / modules.c
index a2865ba..4e1eca9 100644 (file)
 /*
  * modules.c   Radius module support.
  *
- * Author:     Alan DeKok <aland@ox.org>
- *
  * Version:    $Id$
  *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright 2003  The FreeRADIUS server project
+ * Copyright 2000  Alan DeKok <aland@ox.org>
+ * Copyright 2000  Alan Curry <pacman@world.std.com>
  */
 
 static const char rcsid[] = "$Id$";
 
-#include       "autoconf.h"
-#include       "libradius.h"
+#include <freeradius-devel/autoconf.h>
 
-#include       <stdio.h>
-#include       <stdlib.h>
-#include       <string.h>
-#include       <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 
-#include       "radiusd.h"
-#include       "modules.h"
-#include       "conffile.h"
-#include       "ltdl.h"
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modpriv.h>
+#include <freeradius-devel/modules.h>
+#include <freeradius-devel/modcall.h>
+#include <freeradius-devel/conffile.h>
+#include "ltdl.h"
+#include <freeradius-devel/rad_assert.h>
 
-/*
- *     Keep track of which modules we've loaded.
- */
-typedef struct module_list_t {
-       struct module_list_t    *next;
-       char                    name[MAX_STRING_LEN];
-       module_t                *module;
-       lt_dlhandle             handle;
-} module_list_t;
+typedef struct indexed_modcallable {
+       struct indexed_modcallable *next;
+       int idx;
+       modcallable *modulelist;
+} indexed_modcallable;
 
 /*
- *     Internal list of all of the modules we have loaded.
+ *     For each component, keep an ordered list of ones to call.
  */
-static module_list_t *module_list = NULL;
+static indexed_modcallable *components[RLM_COMPONENT_COUNT];
 
-/*
- *     Per-instance data structure, to correlate the modules
- *     with the instance names (may NOT be the module names!), 
- *     and the per-instance data structures.
- */
-typedef struct module_instance_t {
-       struct module_instance_t *next;
-       char                    name[MAX_STRING_LEN];
-       module_list_t           *entry;
-        void                    *insthandle;
-} module_instance_t;
+static rbtree_t *module_tree = NULL;
 
-/*
- *     Internal list of each module instance.
- */
-static module_instance_t *module_instance_list = NULL;
+typedef struct section_type_value_t {
+       const char      *section;
+       const char      *typename;
+       int             attr;
+} section_type_value_t;
 
-/*
- *     For each authorize/authenticate/etc, we have an ordered
- *     list of instances to call.  This data structure keeps track
- *     of that order.
- */
-typedef struct config_module_t {
-       struct config_module_t  *next;
-       int index;
-       module_instance_t       *instance;
-#if HAVE_PTHREAD_H
-       pthread_mutex_t         *mutex;
-#endif
-} config_module_t;
 
 /*
- *     For each component, keep an ordered list of ones to call.
+ *     Ordered by component
  */
-static config_module_t *components[RLM_COMPONENT_COUNT];
+static const section_type_value_t section_type_value[RLM_COMPONENT_COUNT] = {
+       { "authenticate", "Auth-Type",       PW_AUTH_TYPE },
+       { "authorize",    "Autz-Type",       PW_AUTZ_TYPE },
+       { "preacct",      "Pre-Acct-Type",   PW_PRE_ACCT_TYPE },
+       { "accounting",   "Acct-Type",       PW_ACCT_TYPE },
+       { "session",      "Session-Type",    PW_SESSION_TYPE },
+       { "pre-proxy",    "Pre-Proxy-Type",  PW_PRE_PROXY_TYPE },
+       { "post-proxy",   "Post-Proxy-Type", PW_POST_PROXY_TYPE },
+       { "post-auth",    "Post-Auth-Type",  PW_POST_AUTH_TYPE },
+};
 
 /*
- *     The component names.
- *
- *     Hmm... we probably should be getting these from the configuration
- *     file, too.
+ *     Delete ASAP.
  */
-static const char *component_names[RLM_COMPONENT_COUNT] =
-{
-  "authenticate",
-  "authorize",
-  "preacct",
-  "accounting",
-  "session"
+static const section_type_value_t old_section_type_value[] = {
+       { "authenticate", "authtype", PW_AUTH_TYPE },
+       { "authorize",    "autztype", PW_AUTZ_TYPE },
+       { "preacct",      "Pre-Acct-Type",   PW_PRE_ACCT_TYPE },/* unused */
+       { "accounting",   "acctype", PW_ACCT_TYPE },
+       { "session",      "sesstype", PW_SESSION_TYPE },
+       { "pre-proxy",    "Pre-Proxy-Type",  PW_PRE_PROXY_TYPE }, /* unused */
+       { "post-proxy",   "Post-Proxy-Type", PW_POST_PROXY_TYPE }, /* unused */
+       { "post-auth",    "post-authtype", PW_POST_AUTH_TYPE }
 };
 
-static void config_list_free(config_module_t **cf)
+
+static void indexed_modcallable_free(indexed_modcallable **cf)
 {
-       config_module_t *c, *next;
+       indexed_modcallable     *c, *next;
 
        c = *cf;
        while (c) {
-#if HAVE_PTHREAD_H
-               if (c->mutex) {
-                       /*
-                        *      The mutex MIGHT be locked...
-                        *      we'll check for that later, I guess.
-                        */
-                       pthread_mutex_destroy(c->mutex);
-                       free(c->mutex);
-               }
-#endif
                next = c->next;
+               modcallable_free(&c->modulelist);
                free(c);
                c = next;
        }
        *cf = NULL;
 }
 
-static void instance_list_free(module_instance_t **i)
+/*
+ *     Free a module instance.
+ */
+static void module_instance_free(void *data)
 {
-       module_instance_t       *c, *next;
+       module_instance_t *this = data;
 
-       c = *i;
-       while (c) {
-               next = c->next;
-               if(c->entry->module->detach)
-                       (c->entry->module->detach)(c->insthandle);
-               free(c);
-               c = next;
+       if (this->entry->module->detach)
+               (this->entry->module->detach)(this->insthandle);
+#ifdef HAVE_PTHREAD_H
+       if (this->mutex) {
+               /*
+                *      FIXME
+                *      The mutex MIGHT be locked...
+                *      we'll check for that later, I guess.
+                */
+               pthread_mutex_destroy(this->mutex);
+               free(this->mutex);
        }
-       *i = NULL;
+#endif
+       free(this);
 }
 
-static void module_list_free(void)
-{
-       module_list_t *ml, *next;
-       int i;
 
-       /*
-        *      Delete the internal component pointers.
-        */
-       for (i = 0; i < RLM_COMPONENT_COUNT; i++) {
-               config_list_free(&components[i]);
-       }
+/*
+ *     Compare two module entries
+ */
+static int module_entry_cmp(const void *one, const void *two)
+{
+       const module_entry_t *a = one;
+       const module_entry_t *b = two;
 
-       instance_list_free(&module_instance_list);
+       return strcmp(a->name, b->name);
+}
 
-       ml = module_list;
-       while (ml) {
-               next = ml->next;
-               if (ml->module->destroy)
-                       (ml->module->destroy)();
-               lt_dlclose(ml->handle); /* ignore any errors */
-               free(ml);
-               ml = next;
-       }
+/*
+ *     Free a module entry.
+ */
+static void module_entry_free(void *data)
+{
+       module_entry_t *this = data;
 
-       module_list = NULL;
+       lt_dlclose(this->handle);       /* ignore any errors */
+       free(this);
 }
 
+
 /*
- *  New Auth-Type's start at a large number, and go up from there.
- *
- *  We could do something more intelligent, but this should work almost
- *  all of the time.
- *
- * FIXME: move this to dict.c as dict_valadd() and dict_valdel()
- *        also clear value in module_list free (necessary?)
+ *     Remove the module lists.
  */
-static int new_authtype_value(const char *name)
+int detach_modules(void)
 {
-       static int max_value = 32767;
-       DICT_VALUE *old_value, *new_value;
-       
+       int i;
+
        /*
-        *  Check to see if it's already defined.
-        *  If so, return the old value.
+        *      Delete the internal component pointers.
         */
-       old_value = dict_valbyname(name);
-       if (old_value) return old_value->value;
-       
-       /* Look for the predefined Auth-Type value */
-       old_value = dict_valbyattr(PW_AUTHTYPE, 0);
-       if (!old_value) return 0;       /* something WIERD is happening */
-       
-       /* allocate a new value */
-       new_value = (DICT_VALUE *) rad_malloc(sizeof(DICT_VALUE));
-       
-       /* copy the old to the new */
-       memcpy(new_value, old_value, sizeof(DICT_VALUE));
-       old_value->next = new_value;
-       
-       /* set it up */
-       strNcpy(new_value->name, name, sizeof(new_value->name));
-       new_value->value = max_value++;
-       
-       return new_value->value;
+       for (i = 0; i < RLM_COMPONENT_COUNT; i++) {
+               indexed_modcallable_free(&components[i]);
+       }
+
+       return 0;
 }
 
 /*
  *     Find a module on disk or in memory, and link to it.
  */
-static module_list_t *linkto_module(const char *module_name,
-                                   const char *cffilename, int cflineno)
+static module_entry_t *linkto_module(const char *module_name,
+                                    const char *cffilename, int cflineno)
 {
-       module_list_t   **last, *node;
-       lt_dlhandle     *handle;
+       module_entry_t myentry;
+       module_entry_t *node;
+       lt_dlhandle handle;
+       char module_struct[256];
+       char *p;
+       const void *module;
 
-       /*
-        *      Look through the global module library list for the
-        *      named module.
-        */
-       last = &module_list;
-       for (node = module_list; node != NULL; node = node->next) {
-               /*
-                *      Found the named module.  Return it.
-                */
-               if (strcmp(node->name, module_name) == 0)
-                       return node;
-
-               /*
-                *      Keep a pointer to the last entry to update...
-                */
-               last = &node->next;
-       }
+       strNcpy(myentry.name, module_name, sizeof(myentry.name));
+       node = rbtree_finddata(module_tree, &myentry);
+       if (node) return node;
 
        /*
         *      Keep the handle around so we can dlclose() it.
@@ -229,40 +192,61 @@ static module_list_t *linkto_module(const char *module_name,
                return NULL;
        }
 
-       /* make room for the module type */
-       node = (module_list_t *) rad_malloc(sizeof(module_list_t));
-
-       /* fill in the module structure */
-       node->next = NULL;
-       node->handle = handle;
-       strNcpy(node->name, module_name, sizeof(node->name));
-       
        /*
         *      Link to the module's rlm_FOO{} module structure.
+        *
+        *      The module_name variable has the version number
+        *      embedded in it, and we don't want that here.
+        */
+       strcpy(module_struct, module_name);
+       p = strrchr(module_struct, '-');
+       if (p) *p = '\0';
+
+       DEBUG3("    (Loaded %s, checking if it's valid)", module_name);
+
+       /*
+        *      libltld MAY core here, if the handle it gives us contains
+        *      garbage data.
         */
-       node->module = (module_t *) lt_dlsym(node->handle, module_name);
-       if (!node->module) {
+       module = lt_dlsym(handle, module_struct);
+       if (!module) {
                radlog(L_ERR|L_CONS, "%s[%d] Failed linking to "
-                      "%s structure in %s: %s\n",
-                      cffilename, cflineno,
-                      module_name, cffilename, lt_dlerror());
-               lt_dlclose(node->handle);       /* ignore any errors */
-               free(node);
+                               "%s structure in %s: %s\n",
+                               cffilename, cflineno,
+                               module_name, cffilename, lt_dlerror());
+               lt_dlclose(handle);
                return NULL;
        }
-       
-       /* call the modules initialization */
-       if (node->module->init && (node->module->init)() < 0) {
-               radlog(L_ERR|L_CONS, "%s[%d] Module initialization failed.\n",
-                      cffilename, cflineno);
-               lt_dlclose(node->handle);       /* ignore any errors */
-               free(node);
+       /*
+        *      Before doing anything else, check if it's sane.
+        */
+       if ((*(const uint32_t *) module) != RLM_MODULE_MAGIC_NUMBER) {
+               lt_dlclose(handle);
+               radlog(L_ERR|L_CONS, "%s[%d] Invalid version in module '%s'",
+                      cffilename, cflineno, module_name);
                return NULL;
+               
        }
 
+       /* make room for the module type */
+       node = rad_malloc(sizeof(*node));
+       memset(node, 0, sizeof(*node));
+       strNcpy(node->name, module_name, sizeof(node->name));
+       node->module = module;
+       node->handle = handle;
+
        DEBUG("Module: Loaded %s ", node->module->name);
 
-       *last = node;
+       /*
+        *      Add the module as "rlm_foo-version" to the configuration
+        *      section.
+        */
+       if (!rbtree_insert(module_tree, node)) {
+               radlog(L_ERR, "Failed to cache module %s", module_name);
+               lt_dlclose(handle);
+               free(node);
+               return NULL;
+       }
 
        return node;
 }
@@ -270,44 +254,15 @@ static module_list_t *linkto_module(const char *module_name,
 /*
  *     Find a module instance.
  */
-static module_instance_t *find_module_instance(const char *instname)
+module_instance_t *find_module_instance(CONF_SECTION *modules,
+                                       const char *instname)
 {
-       CONF_SECTION *cs, *inst_cs;
+       CONF_SECTION *cs;
        const char *name1, *name2;
-       module_instance_t *node, **last;
+       module_instance_t *node;
        char module_name[256];
 
-       /*
-        *      Look through the global module instance list for the
-        *      named module.
-        */
-       last = &module_instance_list;
-       for (node = module_instance_list; node != NULL; node = node->next) {
-               /*
-                *      Found the named instance.  Return it.
-                */
-               if (strcmp(node->name, instname) == 0)
-                       return node;
-
-               /*
-                *      Keep a pointer to the last entry to update...
-                */
-               last = &node->next;
-       }
-
-       /*
-        *      Instance doesn't exist yet. Try to find the
-        *      corresponding configuration section and create it.
-        */
-
-       /*
-        *      Look for the 'modules' configuration section.
-        */
-       cs = cf_section_find("modules");
-       if (!cs) {
-               radlog(L_ERR|L_CONS, "ERROR: Cannot find a 'modules' section in the configuration file.\n");
-               return NULL;
-       }
+       if (!modules) return NULL;
 
        /*
         *      Module instances are declared in the modules{} block
@@ -315,94 +270,70 @@ static module_instance_t *find_module_instance(const char *instname)
         *      name2 from the config section, or name1 if there was
         *      no name2.
         */
-
-       for(inst_cs=cf_subsection_find_next(cs, NULL, NULL)
-           ; inst_cs ;
-           inst_cs=cf_subsection_find_next(cs, inst_cs, NULL)) {
-                name1 = cf_section_name1(inst_cs);
-                name2 = cf_section_name2(inst_cs);
-               if ( (name2 && !strcmp(name2, instname)) ||
-                    (!name2 && !strcmp(name1, instname)) )
-                       break;
-       }
-       if (!inst_cs) {
+       cs = cf_section_sub_find_name2(modules, NULL, instname);
+       if (cs == NULL) {
                radlog(L_ERR|L_CONS, "ERROR: Cannot find a configuration entry for module \"%s\".\n", instname);
                return NULL;
        }
 
        /*
+        *      If there's already a module instance, return it.
+        */
+       node = cf_data_find(cs, "instance");
+       if (node) return node;
+
+       name1 = cf_section_name1(cs);
+       name2 = cf_section_name2(cs);
+
+       /*
         *      Found the configuration entry.
         */
        node = rad_malloc(sizeof(*node));
-       node->next = NULL;
+       memset(node, 0, sizeof(*node));
+
        node->insthandle = NULL;
-       
+
        /*
-        *      Link to the module by name: rlm_FOO
+        *      Names in the "modules" section aren't prefixed
+        *      with "rlm_", so we add it here.
         */
        snprintf(module_name, sizeof(module_name), "rlm_%s", name1);
+
        node->entry = linkto_module(module_name,
-                                  "radiusd.conf", cf_section_lineno(inst_cs));
+                                   mainconfig.radiusd_conf,
+                                   cf_section_lineno(cs));
        if (!node->entry) {
                free(node);
                /* linkto_module logs any errors */
                return NULL;
        }
-       
+
        /*
         *      Call the module's instantiation routine.
         */
        if ((node->entry->module->instantiate) &&
-           ((node->entry->module->instantiate)(inst_cs,
-                                              &node->insthandle) < 0)) {
+           ((node->entry->module->instantiate)(cs, &node->insthandle) < 0)) {
                radlog(L_ERR|L_CONS,
-                      "radiusd.conf[%d]: %s: Module instantiation failed.\n",
-                      cf_section_lineno(inst_cs), instname);
+                               "%s[%d]: %s: Module instantiation failed.\n",
+                      mainconfig.radiusd_conf, cf_section_lineno(cs),
+                      instname);
                free(node);
                return NULL;
        }
-       
+
        /*
         *      We're done.  Fill in the rest of the data structure,
         *      and link it to the module instance list.
         */
        strNcpy(node->name, instname, sizeof(node->name));
 
-       *last = node;
-
-       DEBUG("Module: Instantiated %s (%s) ", name1, node->name);
-       
-       return node;
-}
-
-/*
- *     Add one entry at the end of the config_module_t list.
- */
-static void add_to_list(int comp, module_instance_t *instance, int index)
-{
-       config_module_t *node;
-       config_module_t **last;
-       config_module_t **head;
-       
-       head = &components[comp];
-       last = head;
-
-       for (node = *head; node != NULL; node = node->next) {
-               last = &node->next;
-       }
-
-       node = (config_module_t *) rad_malloc(sizeof(config_module_t));
-       node->next = NULL;
-       node->instance = instance;
-       node->index = index;
-
-#if HAVE_PTHREAD_H
+#ifdef HAVE_PTHREAD_H
        /*
         *      If we're threaded, check if the module is thread-safe.
         *
         *      If it isn't, we create a mutex.
         */
-       if ((instance->entry->module->type & RLM_TYPE_THREAD_UNSAFE) != 0) {
+       if ((node->entry->module->type & RLM_TYPE_THREAD_UNSAFE) != 0) {
                node->mutex = (pthread_mutex_t *) rad_malloc(sizeof(pthread_mutex_t));
                /*
                 *      Initialize the mutex.
@@ -415,121 +346,264 @@ static void add_to_list(int comp, module_instance_t *instance, int index)
                node->mutex = NULL;
        }
 
-#endif 
+#endif
+       cf_data_add(cs, "instance", node, module_instance_free);
 
-       *last = node;
+       DEBUG("Module: Instantiated %s (%s) ", name1, node->name);
+
+       return node;
 }
 
-static config_module_t *lookup_by_index(config_module_t *head, int index)
+static indexed_modcallable *lookup_by_index(indexed_modcallable *head, int idx)
 {
-       config_module_t *p;
+       indexed_modcallable *p;
 
        for (p = head; p != NULL; p = p->next) {
-               if( p->index == index)
+               if( p->idx == idx)
                        return p;
        }
        return NULL;
 }
 
-static void load_module_section(CONF_SECTION *cs, int comp, const char *filename)
+/*
+ *     Create a new sublist.
+ */
+static indexed_modcallable *new_sublist(int comp, int idx)
+{
+       indexed_modcallable **head = &components[comp];
+       indexed_modcallable *node = *head;
+       indexed_modcallable **last = head;
+
+       while (node) {
+               /* It is an error to try to create a sublist that already
+                * exists. It would almost certainly be caused by accidental
+                * duplication in the config file.
+                *
+                * index 0 is the exception, because it is used when we want
+                * to collect _all_ listed modules under a single index by
+                * default, which is currently the case in all components
+                * except authenticate. */
+               if (node->idx == idx) {
+                       if (idx == 0)
+                               return node;
+                       else
+                               return NULL;
+               }
+               last = &node->next;
+               node = node->next;
+       }
+
+       node = rad_malloc(sizeof *node);
+       node->next = NULL;
+       node->modulelist = NULL;
+       node->idx = idx;
+       *last = node;
+       return node;
+}
+
+static int indexed_modcall(int comp, int idx, REQUEST *request)
+{
+       indexed_modcallable *this;
+
+       this = lookup_by_index(components[comp], idx);
+       if (!this) {
+               if (idx != 0) DEBUG2("  ERROR: Unknown value specified for %s.  Cannot perform requested action.",
+                                    section_type_value[comp].typename);
+               /* Return a default value appropriate for the component */
+               switch(comp) {
+                       case RLM_COMPONENT_AUTZ:    return RLM_MODULE_NOTFOUND;
+                       case RLM_COMPONENT_AUTH:    return RLM_MODULE_REJECT;
+                       case RLM_COMPONENT_PREACCT: return RLM_MODULE_NOOP;
+                       case RLM_COMPONENT_ACCT:    return RLM_MODULE_NOOP;
+                       case RLM_COMPONENT_SESS:    return RLM_MODULE_FAIL;
+                       case RLM_COMPONENT_PRE_PROXY:  return RLM_MODULE_NOOP;
+                       case RLM_COMPONENT_POST_PROXY: return RLM_MODULE_NOOP;
+                       case RLM_COMPONENT_POST_AUTH:  return RLM_MODULE_NOOP;
+                       default:                    return RLM_MODULE_FAIL;
+               }
+       }
+
+       DEBUG2("  Processing the %s section of %s",
+              section_type_value[comp].section, mainconfig.radiusd_conf);
+       return modcall(comp, this->modulelist, request);
+}
+
+/*
+ *     Load a sub-module list, as found inside an Auth-Type foo {}
+ *     block
+ */
+static int load_subcomponent_section(CONF_SECTION *cs, int comp,
+                                    const char *filename)
 {
-       module_instance_t *this;
-       CONF_ITEM       *modref;
-        int            modreflineno;
-        const char     *modrefname;
+       indexed_modcallable *subcomp;
+       modcallable *ml;
+       DICT_VALUE *dval;
+       const char *name2 = cf_section_name2(cs);
 
-       for(modref=cf_item_find_next(cs, NULL)
-           ; modref ;
-           modref=cf_item_find_next(cs, modref)) {
+       rad_assert(comp >= RLM_COMPONENT_AUTH);
+       rad_assert(comp <= RLM_COMPONENT_COUNT);
 
-               if(cf_item_is_section(modref)) {
+       /*
+        *      Sanity check.
+        */
+       if (!name2) {
+               radlog(L_ERR|L_CONS,
+                      "%s[%d]: No name specified for %s block",
+                      filename, cf_section_lineno(cs),
+                      section_type_value[comp].typename);
+               return 1;
+       }
+
+       /*
+        *      Compile the group.
+        */
+       ml = compile_modgroup(comp, cs, filename);
+       if (!ml) {
+               return 0;
+       }       
+
+       /*
+        *      We must assign a numeric index to this subcomponent.
+        *      It is generated and placed in the dictionary by
+        *      setup_modules(), when it loads the sections.  If it
+        *      isn't found, it's a serious error.
+        */
+       dval = dict_valbyname(section_type_value[comp].attr, name2);
+       if (!dval) {
+               radlog(L_ERR|L_CONS,
+                      "%s[%d] %s %s Not previously configured",
+                      filename, cf_section_lineno(cs),
+                      section_type_value[comp].typename, name2);
+               modcallable_free(&ml);
+               return 0;
+       }
+
+       subcomp = new_sublist(comp, dval->value);
+       if (!subcomp) {
+               radlog(L_ERR|L_CONS,
+                      "%s[%d] %s %s already configured - skipping",
+                      filename, cf_section_lineno(cs),
+                      section_type_value[comp].typename, name2);
+               modcallable_free(&ml);
+               return 1;
+       }
+
+       subcomp->modulelist = ml;
+       return 1;               /* OK */
+}
+
+static int load_component_section(CONF_SECTION *cs, int comp,
+                                 const char *filename)
+{
+       modcallable *this;
+       CONF_ITEM *modref;
+       int idx;
+       indexed_modcallable *subcomp;
+       const char *modname;
+       const char *visiblename;
+
+       /*
+        *      Loop over the entries in the named section.
+        */
+       for (modref = cf_item_find_next(cs, NULL);
+            modref != NULL;
+            modref = cf_item_find_next(cs, modref)) {
+               /*
+                *      Look for Auth-Type foo {}, which are special
+                *      cases of named sections, and allowable ONLY
+                *      at the top-level.
+                *
+                *      i.e. They're not allowed in a "group" or "redundant"
+                *      subsection.
+                */
+               if (cf_item_is_section(modref)) {
+                       const char *sec_name;
                        CONF_SECTION *scs;
                        scs = cf_itemtosection(modref);
-                       modreflineno = cf_section_lineno(scs);
-                       modrefname = cf_section_name1(scs);
+
+                       sec_name = cf_section_name1(scs);
+
+                       if (strcmp(sec_name,
+                                  section_type_value[comp].typename) == 0) {
+                               if (!load_subcomponent_section(scs, comp,
+                                                              filename)) {
+                                       return -1; /* FIXME: memleak? */
+                               }
+                               continue;
+                       }
+
+                       /*
+                        *      Allow old names, too.
+                        */
+                       if (strcmp(sec_name,
+                                  old_section_type_value[comp].typename) == 0) {
+                               if (!load_subcomponent_section(scs, comp,
+                                                              filename)) {
+                                       return -1; /* FIXME: memleak? */
+                               }
+                               continue;
+                       }
                } else {
                        CONF_PAIR *cp;
                        cp = cf_itemtopair(modref);
-                       modreflineno = cf_pair_lineno(cp);
-                       modrefname = cf_pair_attr(cp);
                }
 
                /*
-                *      Find an instance for this module.
-                *      This means link to one if it already exists,
-                *      or instantiate one, or load the library and
-                *      instantiate/link.
+                *      Try to compile one entry.
                 */
-               this = find_module_instance(modrefname);
-               if (this == NULL) {
-                       /* find_module_instance logs any errors */
-                       exit(1);
+               this = compile_modsingle(comp, modref, filename, &modname);
+               if (!this) {
+                       radlog(L_ERR|L_CONS,
+                              "%s[%d] Failed to parse %s section.\n",
+                              filename, cf_section_lineno(cs),
+                              cf_section_name1(cs));
+                       return -1;
                }
 
-               switch (comp) {
-               case RLM_COMPONENT_AUTH:
-                       if (!this->entry->module->authenticate) {
-                               radlog(L_ERR|L_CONS,
-                                       "%s[%d] Module %s does not contain "
-                                       "an 'authenticate' entry\n",
-                                       filename, modreflineno,
-                                       this->entry->module->name);
-                               exit(1);
-                       }
-                       add_to_list(RLM_COMPONENT_AUTH, this, new_authtype_value(this->name));
-                       break;
-               case RLM_COMPONENT_AUTZ:
-                       if (!this->entry->module->authorize) {
-                               radlog(L_ERR|L_CONS,
-                                       "%s[%d] Module %s does not contain "
-                                       "an 'authorize' entry\n",
-                                       filename, modreflineno,
-                                       this->entry->module->name);
-                               exit(1);
-                       }
-                       add_to_list(RLM_COMPONENT_AUTZ, this, 0);
-                       break;
-               case RLM_COMPONENT_PREACCT:
-                       if (!this->entry->module->preaccounting) {
-                               radlog(L_ERR|L_CONS,
-                                       "%s[%d] Module %s does not contain "
-                                       "a 'preacct' entry\n",
-                                       filename, modreflineno,
-                                       this->entry->module->name);
-                               exit(1);
-                       }
-                       add_to_list(RLM_COMPONENT_PREACCT, this, 0);
-                       break;
-               case RLM_COMPONENT_ACCT:
-                       if (!this->entry->module->accounting) {
-                               radlog(L_ERR|L_CONS,
-                                       "%s[%d] Module %s does not contain "
-                                       "an 'accounting' entry\n",
-                                       filename, modreflineno,
-                                       this->entry->module->name);
-                               exit(1);
-                       }
-                       add_to_list(RLM_COMPONENT_ACCT, this, 0);
-                       break;
-               case RLM_COMPONENT_SESS:
-                       if (!this->entry->module->checksimul) {
-                               radlog(L_ERR|L_CONS,
-                                       "%s[%d] Module %s does not contain "
-                                       "a 'checksimul' entry\n",
-                                       filename, modreflineno,
-                                       this->entry->module->name);
-                               exit(1);
+               if (comp == RLM_COMPONENT_AUTH) {
+                       DICT_VALUE *dval;
+
+                       dval = dict_valbyname(PW_AUTH_TYPE, modname);
+                       if (!dval) {
+                               /*
+                                *      It's a section, but nothing we
+                                *      recognize.  Die!
+                                */
+                               radlog(L_ERR|L_CONS, "%s[%d] Unknown Auth-Type \"%s\" in %s section.",
+                                      filename, cf_section_lineno(cs),
+                                      modname, section_type_value[comp].section);
+                               return -1;
                        }
-                       add_to_list(RLM_COMPONENT_SESS, this, 0);
-                       break;
-               default:
-                       radlog(L_ERR|L_CONS, "%s[%d] Unknown component %d.\n",
-                               filename, modreflineno, comp);
-                       exit(1);
+                       idx = dval->value;
+               } else {
+                       /* See the comment in new_sublist() for explanation
+                        * of the special index 0 */
+                       idx = 0;
                }
-       }
+
+               subcomp = new_sublist(comp, idx);
+               if (subcomp == NULL) {
+                       radlog(L_INFO|L_CONS,
+                                       "%s %s %s already configured - skipping",
+                                       filename, section_type_value[comp].typename,
+                                       modname);
+                       modcallable_free(&this);
+                       continue;
+               }
+
+               /* If subcomp->modulelist is NULL, add_to_modcallable will
+                * create it */
+               visiblename = cf_section_name2(cs);
+               if (visiblename == NULL)
+                       visiblename = cf_section_name1(cs);
+               add_to_modcallable(&subcomp->modulelist, this,
+                                  comp, visiblename);
+       }
+
+       return 0;
 }
 
+
 /*
  *     Parse the module config sections, and load
  *     and call each module's init() function.
@@ -537,41 +611,41 @@ static void load_module_section(CONF_SECTION *cs, int comp, const char *filename
  *     Libtool makes your life a LOT easier, especially with libltdl.
  *     see: http://www.gnu.org/software/libtool/
  */
-int setup_modules(void)
+int setup_modules(int reload)
 {
        int             comp;
-       CONF_SECTION    *cs;
-        const char *filename="radiusd.conf";
+       CONF_SECTION    *cs, *modules;
+       int             do_component[RLM_COMPONENT_COUNT];
+       rad_listen_t    *listener;
 
        /*
-        *      No current list of modules: Go initialize libltdl.
+        *      If necessary, initialize libltdl.
         */
-       if (!module_list) {
-               if (lt_dlinit() != 0) {
-                       radlog(L_ERR|L_CONS, "Failed to initialize libraries: %s\n",
-                               lt_dlerror());
-                       exit(1); /* FIXME */
-                       
-               }
-
+       if (!reload) {
                /*
                 *      Set the default list of preloaded symbols.
                 *      This is used to initialize libltdl's list of
-                *      preloaded modules. 
+                *      preloaded modules.
                 *
                 *      i.e. Static modules.
                 */
                LTDL_SET_PRELOADED_SYMBOLS();
 
+               if (lt_dlinit() != 0) {
+                       radlog(L_ERR|L_CONS, "Failed to initialize libraries: %s\n",
+                                       lt_dlerror());
+                       return -1;
+               }
+
                /*
                 *      Set the search path to ONLY our library directory.
                 *      This prevents the modules from being found from
                 *      any location on the disk.
                 */
                lt_dlsetsearchpath(radlib_dir);
-               
+
                DEBUG2("Module: Library search path is %s",
-                      lt_dlgetsearchpath());
+                               lt_dlgetsearchpath());
 
                /*
                 *      Initialize the components.
@@ -580,227 +654,310 @@ int setup_modules(void)
                        components[comp] = NULL;
                }
 
+               /*
+                *      Set up the internal module struct.
+                */
+               module_tree = rbtree_create(module_entry_cmp,
+                                           module_entry_free, 0);
+               if (!module_tree) {
+                       radlog(L_ERR|L_CONS, "Failed to initialize modules\n");
+                       return -1;
+               }
+               
+
        } else {
-               module_list_free();
+               detach_modules();
        }
 
        /*
-        *      Loop over all of the known components, finding their
-        *      configuration section, and loading it.
+        *      Figure out which sections to load.
         */
-       for (comp = 0; comp < RLM_COMPONENT_COUNT; ++comp) {
-               cs = cf_section_find(component_names[comp]);
-               if (!cs) continue;
-               
-               load_module_section(cs, comp, filename);
-       }
+       memset(do_component, 0, sizeof(do_component));
+       for (listener = mainconfig.listen;
+            listener != NULL;
+            listener = listener->next) {
+               switch (listener->type) {
+               case RAD_LISTEN_AUTH:
+                       do_component[RLM_COMPONENT_AUTZ] = 1;
+                       do_component[RLM_COMPONENT_AUTH] = 1;
+                       do_component[RLM_COMPONENT_POST_AUTH] = 1;
+                       do_component[RLM_COMPONENT_SESS] = 1;
+                       break;
 
-       return 0;
-}
+               case RAD_LISTEN_DETAIL: /* just like acct */
+               case RAD_LISTEN_ACCT:
+                       do_component[RLM_COMPONENT_PREACCT] = 1;
+                       do_component[RLM_COMPONENT_ACCT] = 1;
+                       break;
 
+               case RAD_LISTEN_PROXY:
+                       do_component[RLM_COMPONENT_PRE_PROXY] = 1;
+                       do_component[RLM_COMPONENT_POST_PROXY] = 1;
+                       break;
 
-/*
- *     Update the Stripped-User-Name attribute.
- */
-static void update_username(REQUEST *request, char *newname)
-{
-       VALUE_PAIR *vp;
+               default:
+                       rad_assert(0 == 1);
+                       break;
+               }
+       }
 
-       /*
-        *      If there isn't a Stripped-User-Name attribute,
-        *      go add one, and make it the definitive user name.
-        */
-       if (request->username->attribute != PW_STRIPPED_USER_NAME) {
-               vp = paircreate(PW_STRIPPED_USER_NAME, PW_TYPE_STRING);
-               if (!vp) {
-                       radlog(L_ERR|L_CONS, "no memory");
-                       exit(1);
+       for (comp = RLM_COMPONENT_AUTH; comp < RLM_COMPONENT_COUNT; comp++) {
+               /*
+                *      Have the debugging messages all in one place.
+                */
+               if (!do_component[comp]) {
+                       DEBUG2("modules: Not loading %s{} section",
+                              section_type_value[comp].section);
                }
-               DEBUG2("  authorize: Creating Stripped-User-Name of %s", newname);
-               strcpy((char *)vp->strvalue, newname);
-               vp->length = strlen((char *)vp->strvalue);
-               pairadd(&request->packet->vps, vp);
-               request->username = vp;
-               return;
        }
 
        /*
-        *      There is one, update it in place.
+        *      Create any DICT_VALUE's for the types.  See
+        *      'doc/configurable_failover' for examples of 'authtype'
+        *      used to create new Auth-Type values.  In order to
+        *      let the user create new names, we've got to look for
+        *      those names, and create DICT_VALUE's for them.
         */
-       vp = request->username;
-       DEBUG2("  authorize: Updating Stripped-User-Name from %s to %s",
-              vp->strvalue, newname);
-       strcpy((char *)vp->strvalue, newname);
-       vp->length = strlen((char *)vp->strvalue);
-}
+       for (comp = RLM_COMPONENT_AUTH; comp < RLM_COMPONENT_COUNT; comp++) {
+               int             value;
+               const char      *name2;
+               DICT_ATTR       *dattr;
+               DICT_VALUE      *dval;
+               CONF_SECTION    *sub, *next;
+               CONF_PAIR       *cp;
 
-#if HAVE_PTHREAD_H
-/*
- *     Lock the mutex for the module
- */
-static void safe_lock(config_module_t *instance)
-{
-       if (instance->mutex) pthread_mutex_lock(instance->mutex);
-}
+               /*
+                *      Not needed, don't load it.
+                */
+               if (!do_component[comp]) {
+                       continue;
+               }
+               cs = cf_section_find(section_type_value[comp].section);
 
-/*
- *     Unlock the mutex for the module
- */
-static void safe_unlock(config_module_t *instance)
-{
-       if (instance->mutex) pthread_mutex_unlock(instance->mutex);
-}
-#else
-/*
- *     No threads: these functions become NULL's.
- */
-#define safe_lock(foo)
-#define safe_unlock(foo)
-#endif
+               if (!cs) continue;
 
-/*
- *     Call all authorization modules until one returns
- *     somethings else than RLM_MODULE_OK
- */
-int module_authorize(REQUEST *request)
-{
-       config_module_t *this;
-       int             rcode = RLM_MODULE_OK;
-
-       this = components[RLM_COMPONENT_AUTZ];
-       rcode = RLM_MODULE_OK;
-
-       while (this && rcode == RLM_MODULE_OK) {
-               DEBUG2("  authorize: %s", this->instance->entry->module->name);
-               safe_lock(this);
-               rcode = (this->instance->entry->module->authorize)(
-                        this->instance->insthandle, request);
-               safe_unlock(this);
-               this = this->next;
+               sub = NULL;
+               do {
+                       /*
+                        *      See if there's a sub-section by that
+                        *      name.
+                        */
+                       next = cf_subsection_find_next(cs, sub,
+                                                      section_type_value[comp].typename);
+
+                       /*
+                        *      Allow some old names, too.
+                        */
+                       if (!next && (comp <= 4)) {
+                               next = cf_subsection_find_next(cs, sub,
+                                                              old_section_type_value[comp].typename);
+                       }
+                       sub = next;
+
+                       /*
+                        *      If so, look for it to define a new
+                        *      value.
+                        */
+                       name2 = cf_section_name2(sub);
+                       if (!name2) continue;
+
+
+                       /*
+                        *      If the value already exists, don't
+                        *      create it again.
+                        */
+                       dval = dict_valbyname(section_type_value[comp].attr,
+                                             name2);
+                       if (dval) continue;
+
+                       /*
+                                *      Find the attribute for the value.
+                        */
+                       dattr = dict_attrbyvalue(section_type_value[comp].attr);
+                       if (!dattr) {
+                               radlog(L_ERR, "%s[%d]: No such attribute %s",
+                                      mainconfig.radiusd_conf,
+                                      cf_section_lineno(sub),
+                                      section_type_value[comp].typename);
+                               continue;
+                       }
+
+                       /*
+                        *      Create a new unique value with a
+                        *      meaningless number.  You can't look at
+                        *      it from outside of this code, so it
+                        *      doesn't matter.  The only requirement
+                        *      is that it's unique.
+                        */
+                       do {
+                               value = lrad_rand() & 0x00ffffff;
+                       } while (dict_valbyattr(dattr->attr, value));
+
+                       if (dict_addvalue(name2, dattr->name, value) < 0) {
+                               radlog(L_ERR, "%s", librad_errstr);
+                               return -1;
+                       }
+               } while (sub != NULL);
+
+               /*
+                *      Loop over the non-sub-sections, too.
+                */
+               cp = NULL;
+               do {
+                       /*
+                        *      See if there's a conf-pair by that
+                        *      name.
+                        */
+                       cp = cf_pair_find_next(cs, cp, NULL);
+                       if (!cp) break;
+
+
+                       /*
+                        *      If the value already exists, don't
+                        *      create it again.
+                        */
+                       name2 = cf_pair_attr(cp);
+                       dval = dict_valbyname(section_type_value[comp].attr,
+                                             name2);
+                       if (dval) continue;
+
+                       /*
+                                *      Find the attribute for the value.
+                        */
+                       dattr = dict_attrbyvalue(section_type_value[comp].attr);
+                       if (!dattr) {
+                               radlog(L_ERR, "%s[%d]: No such attribute %s",
+                                      mainconfig.radiusd_conf,
+                                      cf_section_lineno(sub),
+                                      section_type_value[comp].typename);
+                               continue;
+                       }
+
+                       /*
+                        *      Finally, create the new attribute.
+                        */
+                       do {
+                               value = lrad_rand() & 0x00ffffff;
+                       } while (dict_valbyattr(dattr->attr, value));
+                       if (dict_addvalue(name2, dattr->name, value) < 0) {
+                               radlog(L_ERR, "%s", librad_errstr);
+                               return -1;
+                       }
+               } while (cp != NULL);
+       } /* over the sections which can have redundent sub-sections */
+
+       /*
+        *      Remember where the modules were stored.
+        */
+       modules = cf_section_find("modules");
+       if (!modules) {
+               radlog(L_ERR, "Cannot find a \"modules\" section in the configuration file!");
+               return -1;
        }
 
        /*
-        *      Before authenticating the user, update the
-        *      Stripped-User-Name attribute with any additions.
-        *
-        *      No name: nothing to add.
+        *  Look for the 'instantiate' section, which tells us
+        *  the instantiation order of the modules, and also allows
+        *  us to load modules with no authorize/authenticate/etc.
+        *  sections.
         */
-       if (request->username != NULL) {
-               char newname[256];
-               VALUE_PAIR *vp;
+       cs = cf_section_find("instantiate");
+       if (cs != NULL) {
+               CONF_ITEM *ci;
+               CONF_PAIR *cp;
+               module_instance_t *module;
+               const char *name;
 
                /*
-                *      Try to add a prefix
+                *  Loop over the items in the 'instantiate' section.
                 */
-               for (vp = request->config_items; vp != NULL; vp = vp->next) {
-                       switch (vp->attribute) {
-                       default:
-                               break;
-                               
-                       case PW_ADD_PREFIX:
-                               if ((size_t)(vp->length + request->username->length) > sizeof(vp->strvalue)) {
-                                       DEBUG2("\"%s\"+\"%s\" too long",
-                                              vp->strvalue,
-                                              request->username->strvalue);
-                                       continue;
-                               }
-                               strcpy(newname, (char *)vp->strvalue);
-                               strcat(newname, (char *)request->username->strvalue);
-                               update_username(request, newname);
-                               break;
-                               
-                       case PW_ADD_SUFFIX:
-                               if ((size_t)(vp->length + request->username->length) > sizeof(vp->strvalue)) {
-                                       DEBUG2("\"%s\"+\"%s\" too long",
-                                              request->username->strvalue,
-                                              vp->strvalue);
-                                       continue;
-                               }
-                               strcpy(newname,
-                                       (char *)request->username->strvalue);
-                               strcat(newname, (char *)vp->strvalue);
-                               update_username(request, newname);
-                               break;
+               for (ci=cf_item_find_next(cs, NULL);
+                    ci != NULL;
+                    ci=cf_item_find_next(cs, ci)) {
+
+                       /*
+                        *      Skip sections.  They'll be handled
+                        *      later, if they're referenced at all...
+                        */
+                       if (cf_item_is_section(ci)) {
+                               continue;
+                       }
+
+                       cp = cf_itemtopair(ci);
+                       name = cf_pair_attr(cp);
+                       module = find_module_instance(modules, name);
+                       if (!module) {
+                               return -1;
                        }
-               } /* over all configuration items */
+               } /* loop over items in the subsection */
+       } /* if there's an 'instantiate' section. */
 
-               pairdelete(&request->config_items, PW_ADD_PREFIX);
-               pairdelete(&request->config_items, PW_ADD_SUFFIX);
+       /*
+        *      Loop over all of the known components, finding their
+        *      configuration section, and loading it.
+        */
+       for (comp = 0; comp < RLM_COMPONENT_COUNT; ++comp) {
+               cs = cf_section_find(section_type_value[comp].section);
+               if (cs == NULL)
+                       continue;
+
+               if (!do_component[comp]) {
+                       continue;
+               }
+
+               if (load_component_section(cs, comp, mainconfig.radiusd_conf) < 0) {
+                       return -1;
+               }
        }
 
-       return rcode;
+       return 0;
 }
 
 /*
- *     Authenticate a user/password with various methods.
+ *     Call all authorization modules until one returns
+ *     somethings else than RLM_MODULE_OK
  */
-int module_authenticate(int auth_type, REQUEST *request)
+int module_authorize(int autz_type, REQUEST *request)
 {
-       config_module_t *this;
-       int             rcode = RLM_MODULE_FAIL;
-
-       this = lookup_by_index(components[RLM_COMPONENT_AUTH], auth_type);
-
        /*
-        *      Only check the FIRST component.  If we want multiple
-        *      Auth-Types, then this function should be called multiple
-        *      times.
+        *      Older versions of the server would pass proxy requests
+        *      through the 'authorize' sections twice; once when the
+        *      packet was received from the NAS, and again after the
+        *      reply was received from the home server.  Now that we
+        *      have a 'post_proxy' section, the replies from the home
+        *      server should be sent through that, instead of through
+        *      the 'authorize' section again.
         */
-       DEBUG2("  authenticate: %s",
-              this->instance->entry->module->name);
-       safe_lock(this);
-       rcode = (this->instance->entry->module->authenticate)(
-                           this->instance->insthandle, request);
-       safe_unlock(this);
-
-       return rcode;
+       if (request->proxy != NULL) {
+               DEBUG2(" authorize: Skipping authorize in post-proxy stage");
+               return RLM_MODULE_NOOP;
+       }
+
+       return indexed_modcall(RLM_COMPONENT_AUTZ, autz_type, request);
 }
 
+/*
+ *     Authenticate a user/password with various methods.
+ */
+int module_authenticate(int auth_type, REQUEST *request)
+{
+       return indexed_modcall(RLM_COMPONENT_AUTH, auth_type, request);
+}
 
 /*
  *     Do pre-accounting for ALL configured sessions
  */
 int module_preacct(REQUEST *request)
 {
-       config_module_t *this;
-       int             rcode;
-
-       this = components[RLM_COMPONENT_PREACCT];
-       rcode = RLM_MODULE_OK;
-
-       while (this && (rcode == RLM_MODULE_OK)) {
-               DEBUG2("  preacct: %s", this->instance->entry->module->name);
-               safe_lock(this);
-               rcode = (this->instance->entry->module->preaccounting)
-                               (this->instance->insthandle, request);
-               safe_unlock(this);
-               this = this->next;
-       }
-
-       return rcode;
+       return indexed_modcall(RLM_COMPONENT_PREACCT, 0, request);
 }
 
 /*
  *     Do accounting for ALL configured sessions
  */
-int module_accounting(REQUEST *request)
+int module_accounting(int acct_type, REQUEST *request)
 {
-       config_module_t *this;
-       int             rcode;
-
-       this = components[RLM_COMPONENT_ACCT];
-       rcode = RLM_MODULE_OK;
-
-       while (this && (rcode == RLM_MODULE_OK)) {
-               DEBUG2("  accounting: %s", this->instance->entry->module->name);
-               safe_lock(this);
-               rcode = (this->instance->entry->module->accounting)
-                               (this->instance->insthandle, request);
-               safe_unlock(this);
-               this = this->next;
-       }
-
-       return rcode;
+       return indexed_modcall(RLM_COMPONENT_ACCT, acct_type, request);
 }
 
 /*
@@ -808,10 +965,9 @@ int module_accounting(REQUEST *request)
  *
  *     Returns: 0 == OK, 1 == double logins, 2 == multilink attempt
  */
-int module_checksimul(REQUEST *request, int maxsimul)
+int module_checksimul(int sess_type, REQUEST *request, int maxsimul)
 {
-       config_module_t *this;
-       int             rcode;
+       int rcode;
 
        if(!components[RLM_COMPONENT_SESS])
                return 0;
@@ -823,22 +979,37 @@ int module_checksimul(REQUEST *request, int maxsimul)
        request->simul_max = maxsimul;
        request->simul_mpp = 1;
 
-       this = components[RLM_COMPONENT_SESS];
-       rcode = RLM_MODULE_FAIL;
-
-       while (this && (rcode == RLM_MODULE_FAIL)) {
-               DEBUG2("  checksimul: %s", this->instance->entry->module->name);
-               safe_lock(this);
-               rcode = (this->instance->entry->module->checksimul)
-                               (this->instance->insthandle, request);
-               safe_unlock(this);
-               this = this->next;
-       }
+       rcode = indexed_modcall(RLM_COMPONENT_SESS, sess_type, request);
 
-       if(rcode != RLM_MODULE_OK) {
+       if (rcode != RLM_MODULE_OK) {
                /* FIXME: Good spot for a *rate-limited* warning to the log */
                return 0;
        }
 
        return (request->simul_count < maxsimul) ? 0 : request->simul_mpp;
 }
+
+/*
+ *     Do pre-proxying for ALL configured sessions
+ */
+int module_pre_proxy(int type, REQUEST *request)
+{
+       return indexed_modcall(RLM_COMPONENT_PRE_PROXY, type, request);
+}
+
+/*
+ *     Do post-proxying for ALL configured sessions
+ */
+int module_post_proxy(int type, REQUEST *request)
+{
+       return indexed_modcall(RLM_COMPONENT_POST_PROXY, type, request);
+}
+
+/*
+ *     Do post-authentication for ALL configured sessions
+ */
+int module_post_auth(int postauth_type, REQUEST *request)
+{
+       return indexed_modcall(RLM_COMPONENT_POST_AUTH, postauth_type, request);
+}
+