Port fix for #945 from v3.0.x branch
[freeradius.git] / src / main / modcall.c
index 4447749..f2e15ee 100644 (file)
@@ -28,6 +28,7 @@ RCSID("$Id$")
 #include <freeradius-devel/modcall.h>
 #include <freeradius-devel/rad_assert.h>
 
+extern int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p);
 
 /* mutually-recursive static functions need a prototype up front */
 static modcallable *do_compile_modgroup(modcallable *,
@@ -239,6 +240,11 @@ static const char * const comp2str[] = {
        "pre-proxy",
        "post-proxy",
        "post-auth"
+#ifdef WITH_COA
+       ,
+       "recv-coa",
+       "send-coa"
+#endif
 };
 
 #ifdef HAVE_PTHREAD_H
@@ -267,14 +273,28 @@ static void safe_unlock(module_instance_t *instance)
 #define safe_unlock(foo)
 #endif
 
-static int call_modsingle(int component, modsingle *sp, REQUEST *request,
-                         int default_result)
+static int call_modsingle(int component, modsingle *sp, REQUEST *request)
 {
-       int myresult = default_result;
+       int myresult;
+       int blocked;
+
+       rad_assert(request != NULL);
+
+       /*
+        *      If the request should stop, refuse to do anything.
+        */
+       blocked = (request->master_state == REQUEST_STOP_PROCESSING);
+       if (blocked) return RLM_MODULE_NOOP;
 
        RDEBUG3("  modsingle[%s]: calling %s (%s) for request %d",
               comp2str[component], sp->modinst->name,
               sp->modinst->entry->name, request->number);
+
+       if (sp->modinst->force) {
+               myresult = sp->modinst->code;
+               goto fail;
+       }
+
        safe_lock(sp->modinst);
 
        /*
@@ -287,6 +307,17 @@ static int call_modsingle(int component, modsingle *sp, REQUEST *request,
 
        request->module = "";
        safe_unlock(sp->modinst);
+
+       /*
+        *      Wasn't blocked, and now is.  Complain!
+        */
+       blocked = (request->master_state == REQUEST_STOP_PROCESSING);
+       if (blocked) {
+               radlog(L_INFO, "WARNING: Module %s became unblocked for request %u",
+                      sp->modinst->entry->name, request->number);
+       }
+
+ fail:
        RDEBUG3("  modsingle[%s]: returned from %s (%s) for request %d",
               comp2str[component], sp->modinst->name,
               sp->modinst->entry->name, request->number);
@@ -303,7 +334,12 @@ static int default_component_results[RLM_COMPONENT_COUNT] = {
        RLM_MODULE_FAIL,        /* SESS */
        RLM_MODULE_NOOP,        /* PRE_PROXY */
        RLM_MODULE_NOOP,        /* POST_PROXY */
-       RLM_MODULE_NOOP         /* POST_AUTH */
+       RLM_MODULE_NOOP         /* POST_AUTH */
+#ifdef WITH_COA
+       ,
+       RLM_MODULE_NOOP,        /* RECV_COA_TYPE */
+       RLM_MODULE_NOOP         /* SEND_COA_TYPE */
+#endif
 };
 
 
@@ -324,469 +360,528 @@ static const char *group_name[] = {
        "policy"
 };
 
+/* Here's where we recognize all of our keywords: first the rcodes, then the
+ * actions */
+const FR_NAME_NUMBER mod_rcode_table[] = {
+       { "reject",     RLM_MODULE_REJECT       },
+       { "fail",       RLM_MODULE_FAIL  },
+       { "ok",         RLM_MODULE_OK      },
+       { "handled",    RLM_MODULE_HANDLED      },
+       { "invalid",    RLM_MODULE_INVALID      },
+       { "userlock",   RLM_MODULE_USERLOCK     },
+       { "notfound",   RLM_MODULE_NOTFOUND     },
+       { "noop",       RLM_MODULE_NOOP  },
+       { "updated",    RLM_MODULE_UPDATED      },
+       { NULL, 0 }
+};
+
 static const char *modcall_spaces = "++++++++++++++++++++++++++++++++";
 
 #define MODCALL_STACK_MAX (32)
 
+#define MOD_LOG_OPEN_BRACE(_name) RDEBUG2("%.*s%s %s {", depth + 1, modcall_spaces, _name, c->name)
+#define MOD_LOG_CLOSE_BRACE() RDEBUG2("%.*s} # %s %s = %s", depth + 1, modcall_spaces, \
+                                     group_name[c->type], c->name ? c->name : "", \
+                                     fr_int2str(mod_rcode_table, result, "<invalid>"))
+
 /*
  *     Don't call the modules recursively.  Instead, do them
  *     iteratively, and manage the call stack ourselves.
  */
-typedef struct modcall_stack {
-       int pointer;
+typedef struct modcall_stack_entry_t {
+       int result;
+       int priority;
+       modcallable *c;
+} modcall_stack_entry_t;
 
-       int priority[MODCALL_STACK_MAX];
-       int result[MODCALL_STACK_MAX];
-       modcallable *children[MODCALL_STACK_MAX];
-       modcallable *start[MODCALL_STACK_MAX];
-} modcall_stack;
 
+static int modcall_recurse(REQUEST *request, int component, int depth,
+                          modcall_stack_entry_t *entry, int do_next_sibling);
 
 /*
- *     Call a module, iteratively, with a local stack, rather than
- *     recursively.  What did Paul Graham say about Lisp...?
+ *     Call a child of a block.
  */
-int modcall(int component, modcallable *c, REQUEST *request)
+static void modcall_child(REQUEST *request, int component, int depth,
+                         modcall_stack_entry_t *entry, modcallable *c,
+                         int *result, int do_next_sibling)
 {
-       int myresult;
-       modcall_stack stack;
-       modcallable *parent, *child;
-       modsingle *sp;
-       int if_taken, was_if;
+       modcall_stack_entry_t *next;
 
-       if ((component < 0) || (component >= RLM_COMPONENT_COUNT)) {
-               return RLM_MODULE_FAIL;
+       if (depth >= MODCALL_STACK_MAX) {
+               radlog(L_ERR, "Internal sanity check failed: module stack is too deep");
+               exit(1);
+       }
+
+       /*
+        *      Initialize the childs stack frame.
+        */
+       next = entry + 1;
+       next->c = c;
+       next->result = entry->result;
+       next->priority = 0;
+
+       if (!modcall_recurse(request, component,
+                            depth, next, do_next_sibling)) {
+               *result = RLM_MODULE_FAIL;
+                return;
        }
 
-       stack.pointer = 0;
-       stack.priority[0] = 0;
-       stack.children[0] = c;
-       myresult = stack.result[0] = default_component_results[component];
+       *result = next->result;
+
+       return;
+}
+
+/*
+ *     Interpret the various types of blocks.
+ */
+static int modcall_recurse(REQUEST *request, int component, int depth,
+                          modcall_stack_entry_t *entry, int do_next_sibling)
+{
+       int if_taken, was_if;
+       modcallable *c;
+       int result, priority;
+
        was_if = if_taken = FALSE;
+       result = RLM_MODULE_FAIL;
 
-       while (1) {
-               /*
-                *      A module has taken too long to process the request,
-                *      and we've been told to stop processing it.
-                */
-               if ((request->master_state == REQUEST_STOP_PROCESSING) ||
-                   (request->parent &&
-                    (request->parent->master_state == REQUEST_STOP_PROCESSING))) {
-                       myresult = RLM_MODULE_FAIL;
-                       break;
-               }
+redo:
+       priority = -1;
+       c = entry->c;
 
-               child = stack.children[stack.pointer];
-               if (!child) {
-                       myresult = stack.result[stack.pointer];
-                       break;
+       /*
+        *      Nothing more to do.  Return the code and priority
+        *      which was set by the caller.
+        */
+       if (!c) return TRUE;
+
+       /*
+        *      We've been asked to stop.  Do so.
+        */
+       if ((request->master_state == REQUEST_STOP_PROCESSING) ||
+           (request->parent &&
+            (request->parent->master_state == REQUEST_STOP_PROCESSING))) {
+               entry->result = RLM_MODULE_FAIL;
+               entry->priority = 9999;
+               return TRUE;
+       }
+
+       /*
+        *      Handle "if" conditions.
+        */
+       if (c->type == MOD_IF) {
+               int condition;
+               modgroup *g;
+               const char *p;
+
+       mod_if:
+               g = mod_callabletogroup(c);
+               p = c->name;
+
+               RDEBUG2("%.*s? %s %s", depth + 1, modcall_spaces,
+                       group_name[c->type], c->name);
+
+               if (radius_evaluate_condition(request, result,
+                                             0, &p, TRUE, &condition)) {
+                       RDEBUG2("%.*s? %s %s -> %s", depth + 1, modcall_spaces,
+                               group_name[c->type],
+                               c->name, condition ? "TRUE" : "FALSE");
+               } else {
+                       condition = FALSE;
                }
-               parent = child->parent;
 
-#ifdef WITH_UNLANG
-               if ((child->type == MOD_ELSE) || (child->type == MOD_ELSIF)) {
-                       myresult = stack.result[stack.pointer];
-
-                       if (!was_if) { /* error */
-                               RDEBUG2("%.*s ... skipping %s for request %d: No preceding \"if\"",
-                                      stack.pointer + 1, modcall_spaces,
-                                      group_name[child->type],
-                                      request->number);
-                               goto unroll;
-                       }
-                       if (if_taken) {
-                               RDEBUG2("%.*s ... skipping %s for request %d: Preceding \"if\" was taken",
-                                      stack.pointer + 1, modcall_spaces,
-                                      group_name[child->type],
-                                      request->number);
-                               goto unroll;
-                       }
+               /*
+                *      Didn't pass.  Remember that.
+                */
+               if (!condition) {
+                       was_if = TRUE;
+                       if_taken = FALSE;
+                       goto next_sibling;
                }
 
                /*
-                *      "if" or "elsif".  Evaluate the condition.
+                *      We took the "if".  Go recurse into its' children.
                 */
-               if ((child->type == MOD_IF) || (child->type == MOD_ELSIF)) {
-                       int condition = TRUE;
-                       const char *p = child->name;
-
-                       RDEBUG2("%.*s? %s %s",
-                              stack.pointer + 1, modcall_spaces,
-                              (child->type == MOD_IF) ? "if" : "elsif",
-                              child->name);
-
-                       if (radius_evaluate_condition(request, myresult,
-                                                     0, &p, TRUE, &condition)) {
-                               RDEBUG2("%.*s? %s %s -> %s",
-                                      stack.pointer + 1, modcall_spaces,
-                                      (child->type == MOD_IF) ? "if" : "elsif",
-                                      child->name, (condition != FALSE) ? "TRUE" : "FALSE");
-                       } else {
-                               /*
-                                *      This should never happen, the
-                                *      condition is checked when the
-                                *      module section is loaded.
-                                */
-                               condition = FALSE;
-                       }
+               was_if = TRUE;
+               if_taken = TRUE;
+               goto do_children;
+       } /* MOD_IF */
 
-                       if (!condition) {
-                               stack.result[stack.pointer] = myresult;
-                               stack.children[stack.pointer] = NULL;
-                               was_if = TRUE;
-                               if_taken = FALSE;
-                               goto next_section;
-                       } /* else process it as a simple group */
+       /*
+        *      "else" if the previous "if" was taken.
+        *      "if" if the previous if wasn't taken.
+        */
+       if (c->type == MOD_ELSIF) {
+               if (!was_if) goto elsif_error;
+
+               /*
+                *      Like MOD_ELSE, but allow for a later "else"
+                */
+               if (if_taken) {
+                       RDEBUG2("%.*s ... skipping %s for request %d: Preceding \"if\" was taken",
+                               depth + 1, modcall_spaces,
+                               group_name[c->type], request->number);
+                       was_if = TRUE;
+                       if_taken = TRUE;
+                       goto next_sibling;
                }
 
-               if (child->type == MOD_UPDATE) {
-                       int rcode;
-                       modgroup *g = mod_callabletogroup(child);
+               /*
+                *      Check the "if" condition.
+                */
+               goto mod_if;
+       } /* MOD_ELSIF */
 
-                       rcode = radius_update_attrlist(request, g->cs,
-                                                      g->vps, child->name);
-                       if (rcode != RLM_MODULE_UPDATED) {
-                               myresult = rcode;
-                       }
-                       goto handle_result;
-               }
-#endif
-       
-               if (child->type == MOD_REFERENCE) {
-                       modref *mr = mod_callabletoref(child);
-                       const char *server = request->server;
-
-                       if (server == mr->ref_name) {
-                               RDEBUG("WARNING: Suppressing recursive call to server %s", server);
-                               myresult = RLM_MODULE_NOOP;
-                               goto handle_result;
-                       }
-                       
-                       request->server = mr->ref_name;
-                       RDEBUG("server %s { # nested call", mr->ref_name);
-                       myresult = indexed_modcall(component, 0, request);
-                       RDEBUG("} # server %s with nested call", mr->ref_name);
-                       request->server = server;
-                       goto handle_result;
+       /*
+        *      "else" for a preceding "if".
+        */
+       if (c->type == MOD_ELSE) {
+               if (!was_if) { /* error */
+               elsif_error:
+                       RDEBUG2("%.*s ... skipping %s for request %d: No preceding \"if\"",
+                               depth + 1, modcall_spaces,
+                               group_name[c->type], request->number);                  
+                       goto next_sibling;
                }
 
-               if (child->type == MOD_XLAT) {
-                       modxlat *mx = mod_callabletoxlat(child);
-                       char buffer[128];
-
-                       if (!mx->exec) {
-                               radius_xlat(buffer, sizeof(buffer),
-                                           mx->xlat_name, request, NULL);
-                       } else {
-                               RDEBUG("`%s`", mx->xlat_name);
-                               radius_exec_program(mx->xlat_name, request,
-                                                   0, NULL, 0,
-                                                   request->packet->vps,
-                                                   NULL, 1);
-                       }
-                                           
-                       goto handle_result;
+               if (if_taken) {
+                       RDEBUG2("%.*s ... skipping %s for request %d: Preceding \"if\" was taken",
+                               depth + 1, modcall_spaces,
+                               group_name[c->type], request->number);
+                       was_if = FALSE;
+                       if_taken = FALSE;
+                       goto next_sibling;
                }
 
                /*
-                *      Child is a group that has children of it's own.
+                *      We need to process it.  Go do that.
                 */
-               if (child->type != MOD_SINGLE) {
-                       int count = 1;
-                       modcallable *p, *q;
-#ifdef WITH_UNLANG
-                       modcallable *null_case;
-#endif
-                       modgroup *g = mod_callabletogroup(child);
+               was_if = FALSE;
+               if_taken = FALSE;
+               goto do_children;
+       } /* MOD_ELSE */
 
-                       stack.pointer++;
+       /*
+        *      We're no longer processing if/else/elsif.  Reset the
+        *      trackers for those conditions.
+        */
+       was_if = FALSE;
+       if_taken = FALSE;
 
-                       /*
-                        *      Catastrophic error.  This SHOULD have
-                        *      been caught when we were reading in the
-                        *      conf files.
-                        *
-                        *      FIXME: Do so.
-                        */
-                       if (stack.pointer >= MODCALL_STACK_MAX) {
-                               radlog(L_ERR, "Internal sanity check failed: module stack is too deep");
-                               exit(1);
-                       }
+       if (c->type == MOD_SINGLE) {
+               modsingle *sp;
 
-                       stack.priority[stack.pointer] = 0;
-                       stack.result[stack.pointer] = default_component_results[component];
-                       switch (child->type) {
-#ifdef WITH_UNLANG
-                               char buffer[1024];
+               /*
+                *      Process a stand-alone child, and fall through
+                *      to dealing with it's parent.
+                */
+               sp = mod_callabletosingle(c);
+       
+               result = call_modsingle(c->method, sp, request);
+               RDEBUG2("%.*s[%s] = %s", depth + 1, modcall_spaces, c->name ? c->name : "",
+                       fr_int2str(mod_rcode_table, result, "<invalid>"));
+               goto calculate_result;
+       } /* MOD_SINGLE */
 
-                       case MOD_IF:
-                       case MOD_ELSE:
-                       case MOD_ELSIF:
-                       case MOD_CASE:
-#endif
-                       case MOD_GROUP:
-                       case MOD_POLICY: /* same as MOD_GROUP */
-                               stack.children[stack.pointer] = g->children;
-                               break;
+       /*
+        *      Update attribute(s)
+        */
+       if (c->type == MOD_UPDATE) {
+               int rcode;
+               modgroup *g = mod_callabletogroup(c);
 
-                               /*
-                                *      See the "camel book" for why
-                                *      this works.
-                                *
-                                *      If (rand(0..n) < 1), pick the
-                                *      current realm.  We add a scale
-                                *      factor of 65536, to avoid
-                                *      floating point.
-                                */
-                       case MOD_LOAD_BALANCE:
-                       case MOD_REDUNDANT_LOAD_BALANCE:
-                               q = NULL;
-                               for(p = g->children; p; p = p->next) {
-                                       if (!q) {
-                                               q = p;
-                                               count = 1;
-                                               continue;
-                                       }
-
-                                       count++;
-
-                                       if ((count * (fr_rand() & 0xffff)) < (uint32_t) 0x10000) {
-                                               q = p;
-                                       }
-                               }
-                               stack.children[stack.pointer] = q;
-                               break;
+               MOD_LOG_OPEN_BRACE("update");
+               rcode = radius_update_attrlist(request, g->cs,
+                                              g->vps, c->name);
+               if (rcode != RLM_MODULE_UPDATED) {
+                       result = rcode;
+               } else {
+                       result = RLM_MODULE_NOOP;
+               }
+               MOD_LOG_CLOSE_BRACE();
+               goto calculate_result;
+       } /* MOD_IF */
 
-#ifdef WITH_UNLANG
-                       case MOD_SWITCH:
-                               radius_xlat(buffer, sizeof(buffer),
-                                           child->name, request, NULL);
-
-                               null_case = q = NULL;
-                               for(p = g->children; p; p = p->next) {
-                                       if (!p->name) {
-                                               if (!null_case) null_case = p;
-                                               continue;
-                                       }
-                                       if (strcmp(buffer, p->name) == 0) {
-                                               q = p;
-                                               break;
-                                       }
-                               }
+       /*
+        *      Child is a group that has children of it's own.
+        */
+       if ((c->type == MOD_GROUP) || (c->type == MOD_POLICY) ||
+           (c->type == MOD_CASE)) {
+               modgroup *g;
 
-                               if (!q) q = null_case;
+       do_children:
+               g = mod_callabletogroup(c);
 
-                               stack.children[stack.pointer] = q;
-                               break;
-#endif
+               /*
+                *      This should really have been caught in the
+                *      compiler, and the node never generated.  But
+                *      doing that requires changing it's API so that
+                *      it returns a flag instead of the compiled
+                *      MOD_GROUP.
+                */
+               if (!g->children) {
+                       RDEBUG2("%.*s%s %s { ... } # empty sub-section is ignored",
+                               depth + 1, modcall_spaces, group_name[c->type], c->name);
+                       goto next_sibling;
+               }
 
-                       default:
-                               RDEBUG2("Internal sanity check failed in modcall %d", child->type);
-                               exit(1); /* internal sanity check failure */
-                               break;
-                       }
+               MOD_LOG_OPEN_BRACE(group_name[c->type]);
+               modcall_child(request, component,
+                             depth + 1, entry, g->children,
+                             &result, TRUE);
+               MOD_LOG_CLOSE_BRACE();
+               goto calculate_result;
+       } /* MOD_GROUP */
 
+       if (c->type == MOD_SWITCH) {
+               modcallable *this, *found, *null_case;
+               modgroup *g;
+               char buffer[1024];
 
-                       stack.start[stack.pointer] = stack.children[stack.pointer];
+               MOD_LOG_OPEN_BRACE("switch");
 
-                       RDEBUG2("%.*s- entering %s %s {...}",
-                              stack.pointer, modcall_spaces,
-                              group_name[child->type],
-                              child->name ? child->name : "");
+               /*
+                *      If there's no %, it refers to an attribute.
+                *      Otherwise, expand it.
+                */
+               if (!strchr(c->name, '%')) {
+                       VALUE_PAIR *vp = NULL;
 
-                       /*
-                        *      Catch the special case of a NULL group.
-                        */
-                       if (!stack.children[stack.pointer]) {
-                               /*
-                                *      Print message for NULL group
-                                */
-                               RDEBUG2("%.*s- %s %s returns %s",
-                                      stack.pointer + 1, modcall_spaces,
-                                      group_name[child->type],
-                                      child->name ? child->name : "",
-                                      fr_int2str(rcode_table,
-                                                   stack.result[stack.pointer],
-                                                   "??"));
-                               goto do_return;
+                       if (radius_get_vp(request, c->name, &vp) && vp) {
+                               vp_prints_value(buffer,
+                                               sizeof(buffer),
+                                               vp, 0);
+                       } else {
+                               *buffer = '\0';
                        }
-
-                       /*
-                        *      The child may be a group, so we want to
-                        *      recurse into it's children, rather than
-                        *      falling through to the code below.
-                        */
-                       continue;
+               } else {
+                       radius_xlat(buffer, sizeof(buffer),
+                                   c->name, request, NULL);
                }
 
                /*
-                *      Process a stand-alone child, and fall through
-                *      to dealing with it's parent.
+                *      Find either the exact matching name, or the
+                *      "case {...}" statement.
                 */
-               sp = mod_callabletosingle(child);
-
-               myresult = call_modsingle(child->method, sp, request,
-                                         default_component_results[component]);
-       handle_result:
-               RDEBUG2("%.*s[%s] returns %s",
-                      stack.pointer + 1, modcall_spaces,
-                      child->name ? child->name : "",
-                      fr_int2str(rcode_table, myresult, "??"));
+               g = mod_callabletogroup(c);
+               null_case = found = NULL;
+               for (this = g->children; this; this = this->next) {
+                       if (!this->name) {
+                               if (!null_case) null_case = this;
+                               continue;
+                       }
+                       if (strcmp(buffer, this->name) == 0) {
+                               found = this;
+                               break;
+                       }
+               }
+               
+               if (!found) found = null_case;
+               
+               MOD_LOG_OPEN_BRACE(group_name[c->type]);
+               modcall_child(request, component,
+                             depth + 1, entry, found,
+                             &result, TRUE);
+               MOD_LOG_CLOSE_BRACE();
+               goto calculate_result;
+       } /* MOD_SWITCH */
+
+       if ((c->type == MOD_LOAD_BALANCE) ||
+           (c->type == MOD_REDUNDANT_LOAD_BALANCE)) {
+               int count = 0;
+               modcallable *this, *found;
+               modgroup *g;
+
+               MOD_LOG_OPEN_BRACE("load-balance");
+
+               g = mod_callabletogroup(c);
+               found = NULL;
+               for (this = g->children; this; this = this->next) {
+                       if (!found) {
+                               found = this;
+                               count = 1;
+                               continue;
+                       }
+                       count++;
 
-               /*
-                *      This is a bit of a hack...
-                */
-               if (component != RLM_COMPONENT_SESS) request->simul_max = myresult;
+                       if ((count * (fr_rand() & 0xffff)) < (uint32_t) 0x10000) {
+                               found = this;
+                       }
+               }
 
-               /*
-                *      FIXME: Allow modules to push a modcallable
-                *      onto this stack.  This should simplify
-                *      configuration a LOT!
-                *
-                *      Once we do that, we can't do load-time
-                *      checking of the maximum stack depth, and we've
-                *      got to cache the stack pointer before storing
-                *      myresult.
-                *
-                *      Also, if the stack changed, we need to set
-                *      children[ptr] to NULL, and process the next
-                *      entry on the stack, rather than falling
-                *      through to finalize the processing of this
-                *      entry.
-                *
-                *      Don't put "myresult" on the stack here,
-                *      we have to do so with priority.
-                */
+               MOD_LOG_OPEN_BRACE(group_name[c->type]);
+               
+               if (c->type == MOD_LOAD_BALANCE) {
+                       modcall_child(request, component,
+                                     depth + 1, entry, found,
+                                     &result, FALSE);
+                                              
+               } else {
+                       this = found;
+
+                       do {
+                               modcall_child(request, component,
+                                             depth + 1, entry, found,
+                                             &result, FALSE);
+                               if (found->actions[result] == MOD_ACTION_RETURN) {
+                                       priority = -1;
+                                       break;
+                               }
 
-               /*
-                *      We roll back up the stack at this point.
-                */
-       unroll:
-               /*
-                *      The child's action says return.  Do so.
-                */
-               if (child->actions[myresult] == MOD_ACTION_RETURN) {
-                       stack.result[stack.pointer] = myresult;
-                       stack.children[stack.pointer] = NULL;
-                       goto do_return;
+                               this = this->next;
+                               if (!this) this = g->children;
+                       } while (this != found);
                }
+               MOD_LOG_CLOSE_BRACE();
+               goto calculate_result;
+       } /* MOD_LOAD_BALANCE */
 
-               /*
-                *      If "reject", break out of the loop and return
-                *      reject.
-                */
-               if (child->actions[myresult] == MOD_ACTION_REJECT) {
-                       stack.children[stack.pointer] = NULL;
-                       stack.result[stack.pointer] = RLM_MODULE_REJECT;
-                       goto do_return;
-               }
+       /*
+        *      Reference another virtual server.
+        *
+        *      This should really be deleted, and replaced with a
+        *      more abstracted / functional version.
+        */
+       if (c->type == MOD_REFERENCE) {
+               modref *mr = mod_callabletoref(c);
+               char const *server = request->server;
 
-               /*
-                *      Otherwise, the action is a number, the
-                *      preference level of this return code. If no
-                *      higher preference has been seen yet, remember
-                *      this one.
-                */
-               if (child->actions[myresult] >= stack.priority[stack.pointer]) {
-                       stack.result[stack.pointer] = myresult;
-                       stack.priority[stack.pointer] = child->actions[myresult];
+               if (server == mr->ref_name) {
+                       radlog(L_INFO, "WARNING: Suppressing recursive call to server %s", server);
+                       goto next_sibling;
                }
 
+               request->server = mr->ref_name;
+               RDEBUG("server %s { # nested call", mr->ref_name);
+               result = indexed_modcall(component, 0, request);
+               RDEBUG("} # server %s with nested call", mr->ref_name);
+               request->server = server;
+               goto calculate_result;
+       } /* MOD_REFERENCE */
 
-#ifdef WITH_UNLANG
-       next_section:
-#endif
-               /*
-                *      No parent, we must be done.
-                */
-               if (!parent) {
-                       rad_assert(stack.pointer == 0);
-                       myresult = stack.result[0];
-                       break;
+       /*
+        *      xlat a string without doing anything else
+        *
+        *      This should really be deleted, and replaced with a
+        *      more abstracted / functional version.
+        */
+       if (c->type == MOD_XLAT) {
+               modxlat *mx = mod_callabletoxlat(c);
+               char buffer[128];
+
+               if (!mx->exec) {
+                       radius_xlat(buffer, sizeof(buffer),
+                                   mx->xlat_name, request, NULL);
+               } else {
+                       RDEBUG("`%s`", mx->xlat_name);
+                               radius_exec_program(mx->xlat_name, request,
+                                                   0, NULL, 0,
+                                                   EXEC_TIMEOUT,
+                                                   request->packet->vps,
+                                                   NULL, 1);
                }
 
-               rad_assert(child != NULL);
+               goto next_sibling;
+       } /* MOD_XLAT */
+       
+       /*
+        *      Add new module types here.
+        */
 
-               /*
-                *      Go to the "next" child, whatever that is.
-                */
-               switch (parent->type) {
-#ifdef WITH_UNLANG
-                       case MOD_IF:
-                       case MOD_ELSE:
-                       case MOD_ELSIF:
-                       case MOD_CASE:
+calculate_result:
+#if 0
+       RDEBUG("(%s, %d) ? (%s, %d)",
+              fr_int2str(mod_rcode_table, result, "<invalid>"),
+              priority,
+              fr_int2str(mod_rcode_table, entry->result, "<invalid>"),
+              entry->priority);
 #endif
-                       case MOD_GROUP:
-                       case MOD_POLICY: /* same as MOD_GROUP */
-                               stack.children[stack.pointer] = child->next;
-                               break;
 
-#ifdef WITH_UNLANG
-                       case MOD_SWITCH:
-#endif
-                       case MOD_LOAD_BALANCE:
-                               stack.children[stack.pointer] = NULL;
-                               break;
+       /*
+        *      The child's action says return.  Do so.
+        */
+       if ((c->actions[result] == MOD_ACTION_RETURN) &&
+           (priority <= 0)) {
+               entry->result = result;
+               return TRUE;
+       }
 
-                       case MOD_REDUNDANT_LOAD_BALANCE:
-                               if (child->next) {
-                                       stack.children[stack.pointer] = child->next;
-                               } else {
-                                       modgroup *g = mod_callabletogroup(parent);
+       /*
+        *      If "reject", break out of the loop and return
+        *      reject.
+        */
+       if (c->actions[result] == MOD_ACTION_REJECT) {
+               entry->result = RLM_MODULE_REJECT;
+               return TRUE;
+       }
 
-                                       stack.children[stack.pointer] = g->children;
-                               }
-                               if (stack.children[stack.pointer] == stack.start[stack.pointer]) {
-                                       stack.children[stack.pointer] = NULL;
-                               }
-                               break;
-                       default:
-                               RDEBUG2("Internal sanity check failed in modcall  next %d", child->type);
-                               exit(1);
-               }
+       /*
+        *      The array holds a default priority for this return
+        *      code.  Grab it in preference to any unset priority.
+        */
+       if (priority < 0) {
+               priority = c->actions[result];
+       }
 
-               /*
-                *      No child, we're done this group, and we return
-                *      "myresult" to the caller by pushing it back up
-                *      the stack.
-                */
-               if (!stack.children[stack.pointer]) {
-               do_return:
-                       rad_assert(stack.pointer > 0);
-                       myresult = stack.result[stack.pointer];
-                       stack.pointer--;
+       /*
+        *      We're higher than any previous priority, remember this
+        *      return code and priority.
+        */
+       if (priority > entry->priority) {
+               entry->result = result;
+               entry->priority = priority;
+       }
 
-                       if (stack.pointer == 0) break;
+       /*
+        *      If we're processing a "case" statement, we return once
+        *      it's done, rather than going to the next "case" statement.
+        */
+       if (c->type == MOD_CASE) return TRUE;
 
-                       RDEBUG2("%.*s- %s %s returns %s",
-                              stack.pointer + 1, modcall_spaces,
-                              group_name[parent->type],
-                              parent->name ? parent->name : "",
-                              fr_int2str(rcode_table, myresult, "??"));
+next_sibling:
+       if (do_next_sibling) {
+               entry->c = entry->c->next;
 
-#ifdef WITH_UNLANG
-                       if ((parent->type == MOD_IF) ||
-                           (parent->type == MOD_ELSIF)) {
-                               if_taken = was_if = TRUE;
-                       } else {
-                               if_taken = was_if = FALSE;
-                       }
-#endif
+               if (entry->c) goto redo;
+       }
 
-                       /*
-                        *      Unroll the stack.
-                        */
-                       child = stack.children[stack.pointer];
-                       parent = child->parent;
-                       goto unroll;
-               }
+       /*
+        *      And we're done!
+        */
+       return TRUE;
+}
 
-       } /* loop until done */
 
-       return myresult;
-}
+/**
+ * @brief Call a module, iteratively, with a local stack, rather than
+ *     recursively.  What did Paul Graham say about Lisp...?
+ */
+int modcall(int component, modcallable *c, REQUEST *request)
+{
+       modcall_stack_entry_t stack[MODCALL_STACK_MAX];
+
+       if ((component < 0) || (component >= RLM_COMPONENT_COUNT)) {
+               return RLM_MODULE_FAIL;
+       }
+
+       /*
+        *      Set up the initial stack frame.
+        */
+       stack[0].c = c;
+       stack[0].result = default_component_results[component];
+       stack[0].priority = 0;
 
+       /*
+        *      Call the main handler.
+        */
+       if (!modcall_recurse(request, component, 0, &stack[0], TRUE)) {
+               return RLM_MODULE_FAIL;
+       }
+
+       /*
+        *      Return the result.
+        */
+       return stack[0].result;
+}
 
 #if 0
 static const char *action2str(int action)
@@ -809,24 +904,24 @@ static void dump_mc(modcallable *c, int indent)
 
        if(c->type==MOD_SINGLE) {
                modsingle *single = mod_callabletosingle(c);
-               RDEBUG("%.*s%s {", indent, "\t\t\t\t\t\t\t\t\t\t\t",
+               DEBUG("%.*s%s {", indent, "\t\t\t\t\t\t\t\t\t\t\t",
                        single->modinst->name);
-       } else {
+       } else if ((c->type > MOD_SINGLE) && (c->type <= MOD_POLICY)) {
                modgroup *g = mod_callabletogroup(c);
                modcallable *p;
-               RDEBUG("%.*s%s {", indent, "\t\t\t\t\t\t\t\t\t\t\t",
+               DEBUG("%.*s%s {", indent, "\t\t\t\t\t\t\t\t\t\t\t",
                      group_name[c->type]);
                for(p = g->children;p;p = p->next)
                        dump_mc(p, indent+1);
-       }
+       } /* else ignore it for now */
 
        for(i = 0; i<RLM_MODULE_NUMCODES; ++i) {
-               RDEBUG("%.*s%s = %s", indent+1, "\t\t\t\t\t\t\t\t\t\t\t",
+               DEBUG("%.*s%s = %s", indent+1, "\t\t\t\t\t\t\t\t\t\t\t",
                      fr_int2str(rcode_table, i, "??"),
                      action2str(c->actions[i]));
        }
 
-       RDEBUG("%.*s}", indent, "\t\t\t\t\t\t\t\t\t\t\t");
+       DEBUG("%.*s}", indent, "\t\t\t\t\t\t\t\t\t\t\t");
 }
 
 static void dump_tree(int comp, modcallable *c)
@@ -1156,6 +1251,87 @@ defaultactions[RLM_COMPONENT_COUNT][GROUPTYPE_COUNT][RLM_MODULE_NUMCODES] =
                        MOD_ACTION_RETURN       /* updated  */
                }
        }
+#ifdef WITH_COA
+       ,
+       /* recv-coa */
+       {
+               /* group */
+               {
+                       MOD_ACTION_RETURN,      /* reject   */
+                       MOD_ACTION_RETURN,      /* fail     */
+                       3,                      /* ok       */
+                       MOD_ACTION_RETURN,      /* handled  */
+                       MOD_ACTION_RETURN,      /* invalid  */
+                       MOD_ACTION_RETURN,      /* userlock */
+                       1,                      /* notfound */
+                       2,                      /* noop     */
+                       4                       /* updated  */
+               },
+               /* redundant */
+               {
+                       MOD_ACTION_RETURN,      /* reject   */
+                       1,                      /* fail     */
+                       MOD_ACTION_RETURN,      /* ok       */
+                       MOD_ACTION_RETURN,      /* handled  */
+                       MOD_ACTION_RETURN,      /* invalid  */
+                       MOD_ACTION_RETURN,      /* userlock */
+                       MOD_ACTION_RETURN,      /* notfound */
+                       MOD_ACTION_RETURN,      /* noop     */
+                       MOD_ACTION_RETURN       /* updated  */
+               },
+               /* append */
+               {
+                       MOD_ACTION_RETURN,      /* reject   */
+                       1,                      /* fail     */
+                       MOD_ACTION_RETURN,      /* ok       */
+                       MOD_ACTION_RETURN,      /* handled  */
+                       MOD_ACTION_RETURN,      /* invalid  */
+                       MOD_ACTION_RETURN,      /* userlock */
+                       2,                      /* notfound */
+                       MOD_ACTION_RETURN,      /* noop     */
+                       MOD_ACTION_RETURN       /* updated  */
+               }
+       },
+       /* send-coa */
+       {
+               /* group */
+               {
+                       MOD_ACTION_RETURN,      /* reject   */
+                       MOD_ACTION_RETURN,      /* fail     */
+                       3,                      /* ok       */
+                       MOD_ACTION_RETURN,      /* handled  */
+                       MOD_ACTION_RETURN,      /* invalid  */
+                       MOD_ACTION_RETURN,      /* userlock */
+                       1,                      /* notfound */
+                       2,                      /* noop     */
+                       4                       /* updated  */
+               },
+               /* redundant */
+               {
+                       MOD_ACTION_RETURN,      /* reject   */
+                       1,                      /* fail     */
+                       MOD_ACTION_RETURN,      /* ok       */
+                       MOD_ACTION_RETURN,      /* handled  */
+                       MOD_ACTION_RETURN,      /* invalid  */
+                       MOD_ACTION_RETURN,      /* userlock */
+                       MOD_ACTION_RETURN,      /* notfound */
+                       MOD_ACTION_RETURN,      /* noop     */
+                       MOD_ACTION_RETURN       /* updated  */
+               },
+               /* append */
+               {
+                       MOD_ACTION_RETURN,      /* reject   */
+                       1,                      /* fail     */
+                       MOD_ACTION_RETURN,      /* ok       */
+                       MOD_ACTION_RETURN,      /* handled  */
+                       MOD_ACTION_RETURN,      /* invalid  */
+                       MOD_ACTION_RETURN,      /* userlock */
+                       2,                      /* notfound */
+                       MOD_ACTION_RETURN,      /* noop     */
+                       MOD_ACTION_RETURN       /* updated  */
+               }
+       }
+#endif
 };
 
 
@@ -1165,6 +1341,7 @@ static modcallable *do_compile_modupdate(modcallable *parent,
                                         const char *name2)
 {
        int i, ok = FALSE;
+       const char *vp_name;
        modgroup *g;
        modcallable *csingle;
        CONF_ITEM *ci;
@@ -1173,8 +1350,7 @@ static modcallable *do_compile_modupdate(modcallable *parent,
        static const char *attrlist_names[] = {
                "request", "reply", "proxy-request", "proxy-reply",
                "config", "control",
-               "outer.request", "outer.reply",
-               "outer.config", "outer.control",
+               "coa", "coa-reply", "disconnect", "disconnect-reply",
                NULL
        };
 
@@ -1186,8 +1362,13 @@ static modcallable *do_compile_modupdate(modcallable *parent,
                return NULL;
        }
 
+       vp_name = name2;
+       if (strncmp(vp_name, "outer.", 6) == 0) {
+               vp_name += 6;
+       } 
+
        for (i = 0; attrlist_names[i] != NULL; i++) {
-               if (strcmp(name2, attrlist_names[i]) == 0) {
+               if (strcmp(vp_name, attrlist_names[i]) == 0) {
                        ok = TRUE;
                        break;
                }
@@ -1234,6 +1415,7 @@ static modcallable *do_compile_modupdate(modcallable *parent,
                    (vp->operator != T_OP_SUB) &&
                    (vp->operator != T_OP_LE) &&
                    (vp->operator != T_OP_GE) &&
+                   (vp->operator != T_OP_CMP_FALSE) &&
                    (vp->operator != T_OP_SET)) {
                        pairfree(&head);
                        pairfree(&vp);
@@ -1279,6 +1461,9 @@ static modcallable *do_compile_modupdate(modcallable *parent,
        csingle->type = MOD_UPDATE;
        csingle->method = component;
        
+       memcpy(csingle->actions, defaultactions[component][GROUPTYPE_SIMPLE],
+              sizeof(csingle->actions));
+       
        g->grouptype = GROUPTYPE_SIMPLE;
        g->children = NULL;
        g->cs = cs;
@@ -1578,7 +1763,7 @@ static modcallable *do_compile_modsingle(modcallable *parent,
                        if (parent &&
                            ((parent->type == MOD_LOAD_BALANCE) ||
                             (parent->type == MOD_REDUNDANT_LOAD_BALANCE))) {
-                               cf_log_err(ci, "'elsif' cannot be used in this section section.");
+                               cf_log_err(ci, "'elsif' cannot be used in this section.");
                                return NULL;
                        }
 
@@ -1683,6 +1868,7 @@ static modcallable *do_compile_modsingle(modcallable *parent,
                 *      codes.
                 */
        } else {
+               CONF_SECTION *loop;
                CONF_PAIR *cp = cf_itemtopair(ci);
                modrefname = cf_pair_attr(cp);
 
@@ -1709,11 +1895,37 @@ static modcallable *do_compile_modsingle(modcallable *parent,
                cs = cf_section_find("instantiate");
                if (cs) subcs = cf_section_sub_find_name2(cs, NULL,
                                                          modrefname);
-               if (!subcs) {
-                       cs = cf_section_find("policy");
-                       if (cs) subcs = cf_section_sub_find_name2(cs, NULL,
+               if (!subcs &&
+                   (cs = cf_section_find("policy")) != NULL) {
+                       char buffer[256];
+                       
+                       snprintf(buffer, sizeof(buffer), "%s.%s",
+                                modrefname, comp2str[component]);
+
+                       /*
+                        *      Prefer name.section, then name.
+                        */
+                       subcs = cf_section_sub_find_name2(cs, NULL,
+                                                         buffer);
+                       if (!subcs) {
+                               subcs = cf_section_sub_find_name2(cs, NULL,
                                                                  modrefname);
+                       }
                }
+
+               /*
+                *      Allow policies to over-ride module names.
+                *      i.e. the "sql" policy can do some extra things,
+                *      and then call the "sql" module.
+                */
+               for (loop = cf_item_parent(ci);
+                    loop && subcs;
+                    loop = cf_item_parent(cf_sectiontoitem(loop))) {
+                       if (loop == subcs) {
+                               subcs = NULL;
+                       }
+               }
+
                if (subcs) {
                        DEBUG2(" Module: Loading virtual module %s",
                               modrefname);
@@ -1802,7 +2014,7 @@ static modcallable *do_compile_modsingle(modcallable *parent,
                }
                
                *modname = NULL;
-               cf_log_err(ci, "Failed to find module \"%s\".", modrefname);
+               cf_log_err(ci, "Failed to find \"%s\" in the \"modules\" section.", modrefname);
                return NULL;
        } while (0);
 
@@ -1834,21 +2046,22 @@ static modcallable *do_compile_modsingle(modcallable *parent,
         *      maybe a csingle as a ref?
         */
        if (cf_item_is_section(ci)) {
+               CONF_ITEM *csi;
+               
                cs = cf_itemtosection(ci);
+               for (csi=cf_item_find_next(cs, NULL);
+                    csi != NULL;
+                    csi=cf_item_find_next(cs, csi)) {
 
-               for (ci=cf_item_find_next(cs, NULL);
-                    ci != NULL;
-                    ci=cf_item_find_next(cs, ci)) {
-
-                       if (cf_item_is_section(ci)) {
-                               cf_log_err(ci, "Subsection of module instance call not allowed");
+                       if (cf_item_is_section(csi)) {
+                               cf_log_err(csi, "Subsection of module instance call not allowed");
                                modcallable_free(&csingle);
                                return NULL;
                        }
 
-                       if (!cf_item_is_pair(ci)) continue;
+                       if (!cf_item_is_pair(csi)) continue;
 
-                       if (!compile_action(csingle, cf_itemtopair(ci))) {
+                       if (!compile_action(csingle, cf_itemtopair(csi))) {
                                modcallable_free(&csingle);
                                return NULL;
                        }
@@ -2056,11 +2269,15 @@ void add_to_modcallable(modcallable **parent, modcallable *this,
 void modcallable_free(modcallable **pc)
 {
        modcallable *c, *loop, *next;
+
+       if (!pc || !*pc) return;
+
        c = *pc;
-       if (c->type != MOD_SINGLE) {
+
+       if ((c->type > MOD_SINGLE) && (c->type <= MOD_POLICY)) {
                modgroup *g = mod_callabletogroup(c);
 
-               for(loop = g->children;
+               if (g->children) for (loop = g->children;
                    loop ;
                    loop = next) {
                        next = loop->next;