📄 modcall.c
字号:
/* * modcall.c * * Version: $Id: modcall.c,v 1.99 2008/03/16 17:59:28 aland Exp $ * * 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 2000,2006 The FreeRADIUS server project */#include <freeradius-devel/ident.h>RCSID("$Id: modcall.c,v 1.99 2008/03/16 17:59:28 aland Exp $")#include <freeradius-devel/radiusd.h>#include <freeradius-devel/modpriv.h>#include <freeradius-devel/modcall.h>#include <freeradius-devel/rad_assert.h>/* mutually-recursive static functions need a prototype up front */static modcallable *do_compile_modgroup(modcallable *, int, CONF_SECTION *, int, int);/* Actions may be a positive integer (the highest one returned in the group * will be returned), or the keyword "return", represented here by * MOD_ACTION_RETURN, to cause an immediate return. * There's also the keyword "reject", represented here by MOD_ACTION_REJECT * to cause an immediate reject. */#define MOD_ACTION_RETURN (-1)#define MOD_ACTION_REJECT (-2)/* Here are our basic types: modcallable, modgroup, and modsingle. For an * explanation of what they are all about, see ../../doc/README.failover */struct modcallable { modcallable *parent; struct modcallable *next; const char *name; enum { MOD_SINGLE = 1, MOD_GROUP, MOD_LOAD_BALANCE, MOD_REDUNDANT_LOAD_BALANCE, MOD_IF, MOD_ELSE, MOD_ELSIF, MOD_UPDATE, MOD_SWITCH, MOD_CASE, MOD_POLICY, MOD_REFERENCE } type; int method; int actions[RLM_MODULE_NUMCODES];};#define GROUPTYPE_SIMPLE 0#define GROUPTYPE_REDUNDANT 1#define GROUPTYPE_APPEND 2#define GROUPTYPE_COUNT 3typedef struct { modcallable mc; /* self */ int grouptype; /* after mc */ modcallable *children; CONF_SECTION *cs; VALUE_PAIR *vps;} modgroup;typedef struct { modcallable mc; module_instance_t *modinst;} modsingle;typedef struct { modcallable mc; const char *ref_name; CONF_SECTION *ref_cs;} modref;static const FR_NAME_NUMBER grouptype_table[] = { { "", GROUPTYPE_SIMPLE }, { "redundant ", GROUPTYPE_REDUNDANT }, { "append ", GROUPTYPE_APPEND }, { NULL, -1 }};/* Simple conversions: modsingle and modgroup are subclasses of modcallable, * so we often want to go back and forth between them. */static modsingle *mod_callabletosingle(modcallable *p){ rad_assert(p->type==MOD_SINGLE); return (modsingle *)p;}static modgroup *mod_callabletogroup(modcallable *p){ rad_assert((p->type > MOD_SINGLE) && (p->type <= MOD_POLICY)); return (modgroup *)p;}static modcallable *mod_singletocallable(modsingle *p){ return (modcallable *)p;}static modcallable *mod_grouptocallable(modgroup *p){ return (modcallable *)p;}static modref *mod_callabletoref(modcallable *p){ rad_assert(p->type==MOD_REFERENCE); return (modref *)p;}static modcallable *mod_reftocallable(modref *p){ return (modcallable *)p;}/* modgroups are grown by adding a modcallable to the end *//* FIXME: This is O(N^2) */static void add_child(modgroup *g, modcallable *c){ modcallable **head = &g->children; modcallable *node = *head; modcallable **last = head; if (!c) return; while (node) { last = &node->next; node = node->next; } rad_assert(c->next == NULL); *last = c; c->parent = mod_grouptocallable(g);}/* Here's where we recognize all of our keywords: first the rcodes, then the * actions */static const FR_NAME_NUMBER 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 }};/* * Compile action && rcode for later use. */static int compile_action(modcallable *c, CONF_PAIR *cp){ int action; const char *attr, *value; attr = cf_pair_attr(cp); value = cf_pair_value(cp); if (!value) return 0; if (!strcasecmp(value, "return")) action = MOD_ACTION_RETURN; else if (!strcasecmp(value, "break")) action = MOD_ACTION_RETURN; else if (!strcasecmp(value, "reject")) action = MOD_ACTION_REJECT; else if (strspn(value, "0123456789")==strlen(value)) { action = atoi(value); /* * Don't allow priority zero, for future use. */ if (action == 0) return 0; } else { cf_log_err(cf_pairtoitem(cp), "Unknown action '%s'.\n", value); return 0; } if (strcasecmp(attr, "default") != 0) { int rcode; rcode = fr_str2int(rcode_table, attr, -1); if (rcode < 0) { cf_log_err(cf_pairtoitem(cp), "Unknown module rcode '%s'.\n", attr); return 0; } c->actions[rcode] = action; } else { /* set all unset values to the default */ int i; for (i = 0; i < RLM_MODULE_NUMCODES; i++) { if (!c->actions[i]) c->actions[i] = action; } } return 1;}/* Some short names for debugging output */static const char * const comp2str[] = { "authenticate", "authorize", "preacct", "accounting", "session", "pre-proxy", "post-proxy", "post-auth"};#ifdef HAVE_PTHREAD_H/* * Lock the mutex for the module */static void safe_lock(module_instance_t *instance){ if (instance->mutex) pthread_mutex_lock(instance->mutex);}/* * Unlock the mutex for the module */static void safe_unlock(module_instance_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)#endifstatic int call_modsingle(int component, modsingle *sp, REQUEST *request, int default_result){ int myresult = default_result; DEBUG3(" modsingle[%s]: calling %s (%s) for request %d", comp2str[component], sp->modinst->name, sp->modinst->entry->name, request->number); safe_lock(sp->modinst); /* * For logging unresponsive children. */ request->module = sp->modinst->name; myresult = sp->modinst->entry->module->methods[component]( sp->modinst->insthandle, request); request->module = "<server-core>"; safe_unlock(sp->modinst); DEBUG3(" modsingle[%s]: returned from %s (%s) for request %d", comp2str[component], sp->modinst->name, sp->modinst->entry->name, request->number); return myresult;}static int default_component_results[RLM_COMPONENT_COUNT] = { RLM_MODULE_REJECT, /* AUTH */ RLM_MODULE_NOTFOUND, /* AUTZ */ RLM_MODULE_NOOP, /* PREACCT */ RLM_MODULE_NOOP, /* ACCT */ RLM_MODULE_FAIL, /* SESS */ RLM_MODULE_NOOP, /* PRE_PROXY */ RLM_MODULE_NOOP, /* POST_PROXY */ RLM_MODULE_NOOP /* POST_AUTH */};static const char *group_name[] = { "", "single", "group", "load-balance group", "redundant-load-balance group", "if", "else", "elsif", "update", "switch", "case", "policy"};static const char *modcall_spaces = "++++++++++++++++++++++++++++++++";#define MODCALL_STACK_MAX (32)/* * Don't call the modules recursively. Instead, do them * iteratively, and manage the call stack ourselves. */typedef struct modcall_stack { int pointer; int priority[MODCALL_STACK_MAX]; int result[MODCALL_STACK_MAX]; modcallable *children[MODCALL_STACK_MAX]; modcallable *start[MODCALL_STACK_MAX];} modcall_stack;/* * 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){ int myresult; modcall_stack stack; modcallable *parent, *child; modsingle *sp; int if_taken, was_if; if ((component < 0) || (component >= RLM_COMPONENT_COUNT)) { return RLM_MODULE_FAIL; } stack.pointer = 0; stack.priority[0] = 0; stack.children[0] = c; myresult = stack.result[0] = default_component_results[component]; was_if = if_taken = FALSE; 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; } child = stack.children[stack.pointer]; if (!child) { myresult = stack.result[stack.pointer]; break; } parent = child->parent; if ((child->type == MOD_ELSE) || (child->type == MOD_ELSIF)) { myresult = stack.result[stack.pointer]; if (!was_if) { /* error */ DEBUG2("%.*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) { DEBUG2("%.*s ... skipping %s for request %d: Preceding \"if\" was taken", stack.pointer + 1, modcall_spaces, group_name[child->type], request->number); goto unroll; } } /* * "if" or "elsif". Evaluate the condition. */ if ((child->type == MOD_IF) || (child->type == MOD_ELSIF)) { int condition = TRUE; const char *p = child->name; DEBUG2("%.*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)) { DEBUG2("%.*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; } 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 */ } if (child->type == MOD_UPDATE) { int rcode; modgroup *g = mod_callabletogroup(child); rcode = radius_update_attrlist(request, g->cs, g->vps, child->name); if (rcode != RLM_MODULE_UPDATED) { myresult = rcode; } goto handle_result; } if (child->type == MOD_REFERENCE) { modref *mr = mod_callabletoref(child); const char *server = request->server; if (server == mr->ref_name) { DEBUG("WARNING: Suppressing recursive call to server %s", server); myresult = RLM_MODULE_NOOP; goto handle_result; } request->server = mr->ref_name; DEBUG("server %s { # nested call", mr->ref_name); myresult = indexed_modcall(component, 0, request); DEBUG("} # server %s with nested call", mr->ref_name); request->server = server; goto handle_result; } /* * Child is a group that has children of it's own. */ if (child->type != MOD_SINGLE) { int count = 1; modcallable *p, *q, *null_case; modgroup *g = mod_callabletogroup(child); stack.pointer++; /* * 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); } stack.priority[stack.pointer] = 0; stack.result[stack.pointer] = default_component_results[component]; switch (child->type) { char buffer[1024]; case MOD_IF: case MOD_ELSE: case MOD_ELSIF: case MOD_CASE: case MOD_GROUP: case MOD_POLICY: /* same as MOD_GROUP */ stack.children[stack.pointer] = g->children; break; /* * 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. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -