📄 evaluate.c
字号:
/* * evaluate.c Evaluate a policy language * * Version: $Id: evaluate.c,v 1.20 2008/03/07 10:03:35 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 2004 Alan DeKok <aland@ox.org> * Copyright 2006 The FreeRADIUS server project */#include <freeradius-devel/ident.h>RCSID("$Id: evaluate.c,v 1.20 2008/03/07 10:03:35 aland Exp $")#include "rlm_policy.h"#ifdef HAVE_REGEX_H#include <regex.h>#endif#define debug_evaluate if (0) printf/* * Print stuff we've parsed */static void policy_print(const policy_item_t *item, int indent){ if (!item) { if (indent) fprintf(fr_log_fp, "%*s", indent, " "); fprintf(fr_log_fp, "[NULL]\n"); return; } while (item) { switch (item->type) { case POLICY_TYPE_BAD: if (indent) fprintf(fr_log_fp, "%*s", indent, " "); fprintf(fr_log_fp, "[BAD STATEMENT]"); break; case POLICY_TYPE_PRINT: if (indent) fprintf(fr_log_fp, "%*s", indent, " "); { const policy_print_t *this; this = (const policy_print_t *) item; if (this->rhs_type == POLICY_LEX_BARE_WORD) { fprintf(fr_log_fp, "print %s\n", this->rhs); } else { fprintf(fr_log_fp, "print \"%s\"\n", this->rhs); } } break; case POLICY_TYPE_ASSIGNMENT: { const policy_assignment_t *assign; assign = (const policy_assignment_t *) item; if (indent) fprintf(fr_log_fp, "%*s", indent, " "); fprintf(fr_log_fp, "\t%s %s ", assign->lhs, fr_int2str(rlm_policy_tokens, assign->assign, "?")); if (assign->rhs_type == POLICY_LEX_BARE_WORD) { fprintf(fr_log_fp, "%s\n", assign->rhs); } else { /* * FIXME: escape " */ fprintf(fr_log_fp, "\"%s\"\n", assign->rhs); } } break; case POLICY_TYPE_CONDITIONAL: /* no indentation here */ { const policy_condition_t *condition; condition = (const policy_condition_t *) item; fprintf(fr_log_fp, "("); if (condition->sense) { fprintf(fr_log_fp, "!"); } /* * Nested conditions. */ if (condition->compare == POLICY_LEX_L_BRACKET) { policy_print(condition->child, indent); fprintf(fr_log_fp, ")"); break; } if (condition->compare == POLICY_LEX_L_NOT) { fprintf(fr_log_fp, "!"); policy_print(condition->child, indent); fprintf(fr_log_fp, ")"); break; } if (condition->compare == POLICY_LEX_CMP_TRUE) { fprintf(fr_log_fp, "%s)", condition->lhs); break; } if (condition->lhs_type == POLICY_LEX_FUNCTION) { fprintf(fr_log_fp, "%s()", condition->lhs); } else { /* * FIXME: escape ", * and move all of this logic * to a function. */ fprintf(fr_log_fp, "\"%s\"", condition->lhs); } /* * We always print this condition. */ fprintf(fr_log_fp, " %s ", fr_int2str(rlm_policy_tokens, condition->compare, "?")); if (condition->rhs_type == POLICY_LEX_BARE_WORD) { fprintf(fr_log_fp, "%s", condition->rhs); } else { /* * FIXME: escape ", * and move all of this logic * to a function. */ fprintf(fr_log_fp, "\"%s\"", condition->rhs); } fprintf(fr_log_fp, ")"); if ((condition->child_condition != POLICY_LEX_BAD) && (condition->child_condition != POLICY_LEX_BARE_WORD)) { fprintf(fr_log_fp, " %s ", fr_int2str(rlm_policy_tokens, condition->child_condition, "?")); policy_print(condition->child, indent); } } break; case POLICY_TYPE_IF: { const policy_if_t *statement; statement = (const policy_if_t *) item; if (indent) fprintf(fr_log_fp, "%*s", indent, " "); fprintf(fr_log_fp, "if "); policy_print(statement->condition, indent); fprintf(fr_log_fp, " {\n"); policy_print(statement->if_true, indent + 1); if (indent) fprintf(fr_log_fp, "%*s", indent, " "); if (statement->if_false) { fprintf(fr_log_fp, "} else "); if (statement->if_false->type == POLICY_TYPE_ASSIGNMENT) { fprintf(fr_log_fp, " { "); policy_print(statement->if_false, indent + 1); if (indent) fprintf(fr_log_fp, "%*s", indent, " "); fprintf(fr_log_fp, " }"); } else { policy_print(statement->if_false, indent + 1); } } else { fprintf(fr_log_fp, "}\n"); } } break; case POLICY_TYPE_ATTRIBUTE_LIST: { const policy_attributes_t *this; this = (const policy_attributes_t *) item; if (indent) fprintf(fr_log_fp, "%*s", indent, " "); fprintf(fr_log_fp, "%s %s {\n", fr_int2str(policy_reserved_words, this->where, "?"), fr_int2str(rlm_policy_tokens, this->how, "?")); policy_print(this->attributes, indent + 1); if (indent) fprintf(fr_log_fp, "%*s", indent, " "); fprintf(fr_log_fp, "}\n"); } break; case POLICY_TYPE_NAMED_POLICY: { const policy_named_t *this; this = (const policy_named_t *) item; if (indent) fprintf(fr_log_fp, "%*s", indent, " "); fprintf(fr_log_fp, "policy %s {\n", this->name); policy_print(this->policy, indent + 1); if (indent) fprintf(fr_log_fp, "%*s", indent, " "); fprintf(fr_log_fp, "}\n"); } break; case POLICY_TYPE_CALL: { const policy_call_t *this; this = (const policy_call_t *) item; if (indent) fprintf(fr_log_fp, "%*s", indent, " "); fprintf(fr_log_fp, "call %s\n", this->name); } break; case POLICY_TYPE_RETURN: { const policy_return_t *this; this = (const policy_return_t *) item; if (indent) fprintf(fr_log_fp, "%*s", indent, " "); fprintf(fr_log_fp, "return %s\n", fr_int2str(policy_return_codes, this->rcode, "???")); } break; case POLICY_TYPE_MODULE: { const policy_module_t *this; this = (const policy_module_t *) item; if (indent) fprintf(fr_log_fp, "%*s", indent, " "); fprintf(fr_log_fp, "module %s <stuff>\n", fr_int2str(policy_component_names, this->component, "???")); } break; default: if (indent) fprintf(fr_log_fp, "%*s", indent, " "); fprintf(fr_log_fp, "[HUH?]\n"); break; } item = item->next; }}void rlm_policy_print(const policy_item_t *item){ if (!fr_log_fp) return; fprintf(fr_log_fp, "# rlm_policy \n"); policy_print(item, 0);}/* * Internal stack of things to do. This lets us have function * calls... * * Yes, we should learn lex, yacc, etc. */#define POLICY_MAX_STACK 16typedef struct policy_state_t { rlm_policy_t *inst; REQUEST *request; /* so it's not passed on the C stack */ int rcode; /* for functions, etc. */ int component; /* for calling other modules */ int depth; const policy_item_t *stack[POLICY_MAX_STACK];} policy_state_t;static int policy_evaluate_name(policy_state_t *state, const char *name);/* * Push an item onto the state. */static int policy_stack_push(policy_state_t *state, const policy_item_t *item){ rad_assert(state->depth >= 0); /* * Asked to push nothing. Don't push it. */ if (!item) return 1; /* * State is full. Die. */ if (state->depth >= POLICY_MAX_STACK) { return 0; } /* * Walk back up the stack, looking for previous ocurrances * of this name. If found, we have infinite recursion, * which we stop dead in the water! * * This isn't strictly necessary right now, as we look up * policies by name when they're first referenced. This * means that ALL references are backwards (to the start * of the file), which means that there are no circular * references. */ if (item->type == POLICY_TYPE_NAMED_POLICY) { int i; for (i = 0; i < state->depth; i++) { /* * Check for circular references, by seeing * if the function is already on the stack. * * Hmmm... do we want to do this for any type? */ if (state->stack[i] == item) { debug_evaluate("Circular call to policy %s\n", ((const policy_named_t *) item)->name); return 0; } } } debug_evaluate("push %d %p\n", state->depth, item); state->stack[state->depth] = item; state->depth++; /* points to unused entry */ return 1;}/* * Pop an item from the state. */static int policy_stack_pop(policy_state_t *state, const policy_item_t **pitem){ rad_assert(pitem != NULL); rad_assert(state->depth >= 0); redo: if (state->depth == 0) { *pitem = NULL; return 0; } *pitem = state->stack[state->depth - 1]; /* * Named policies are on the stack for catching recursion. */ if ((*pitem)->type == POLICY_TYPE_NAMED_POLICY) { state->depth--; goto redo; } /* * Process the whole item list. */ if ((*pitem)->next) { state->stack[state->depth - 1] = (*pitem)->next; debug_evaluate("pop/push %d %p\n", state->depth - 1, *pitem); } else { state->depth--; /* points to unused entry */ debug_evaluate("pop %d %p\n", state->depth, *pitem); } return 1;}/* * Evaluate a print statement */static int evaluate_print(policy_state_t *state, const policy_item_t *item){ const policy_print_t *this; if (!fr_log_fp) return 1; this = (const policy_print_t *) item; if (this->rhs_type == POLICY_LEX_BARE_WORD) { fprintf(fr_log_fp, "%s\n", this->rhs); } else { char buffer[1024]; radius_xlat(buffer, sizeof(buffer), this->rhs, state->request, NULL); fprintf(fr_log_fp, "%s", buffer); if (!strchr(buffer, '\n')) fprintf(fr_log_fp, "\n"); } /* * Doesn't change state->rcode */ return 1;}/* * Return a VALUE_PAIR, given an attribute name. * * FIXME: Have it return the N'th one, too, like * doc/variables.txt? * * The amount of duplicated code is getting annoying... */static VALUE_PAIR *find_vp(REQUEST *request, const char *name){ const char *p; const DICT_ATTR *dattr; VALUE_PAIR *vps; p = name; vps = request->packet->vps;; /* * FIXME: use names from reserved word list? */ if (strncasecmp(name, "request:", 8) == 0) { p += 8; } else if (strncasecmp(name, "reply:", 6) == 0) { p += 6; vps = request->reply->vps; } else if (strncasecmp(name, "proxy-request:", 14) == 0) { p += 14; if (request->proxy) { vps = request->proxy->vps; } } else if (strncasecmp(name, "proxy-reply:", 12) == 0) { p += 12; if (request->proxy_reply) { vps = request->proxy_reply->vps; } } else if (strncasecmp(name, "control:", 8) == 0) { p += 8; vps = request->config_items; } /* else it must be a bare attribute name */ if (!vps) { return NULL; } dattr = dict_attrbyname(p); if (!dattr) { fprintf(stderr, "No such attribute %s\n", p); return NULL; /* no such attribute */ } return pairfind(vps, dattr->attr);}/* * Evaluate an assignment * * Not really used much... */static int evaluate_assignment(UNUSED policy_state_t *state, const policy_item_t *item){ const policy_assignment_t *this;#if 0 const DICT_ATTR *dattr;#endif this = (const policy_assignment_t *) item; rad_assert(this->lhs != NULL); rad_assert(this->rhs != NULL);#if 0 dattr = dict_attrbyname(this->lhs); if (!dattr) { fprintf(stderr, "HUH?\n"); return 0; }#endif return 1;}/* * Evaluate a condition */static int evaluate_condition(policy_state_t *state, const policy_item_t *item){ int rcode; const policy_condition_t *this; VALUE_PAIR *vp = NULL; const char *data = NULL; int compare;#ifdef HAVE_REGEX_H regex_t reg;#endif char buffer[256]; char lhs_buffer[2048]; this = (const policy_condition_t *) item; redo: /* * FIXME: Don't always do this... */ if (this->compare != POLICY_LEX_L_BRACKET) { if (this->lhs_type == POLICY_LEX_FUNCTION) { /* * We can't call evaluate_call here, * because that just pushes stuff onto * the stack, and we want to actually * evaluate all of it... */ rcode = policy_evaluate_name(state, this->lhs); data = fr_int2str(policy_return_codes, rcode, "???"); strlcpy(lhs_buffer, data, sizeof(lhs_buffer)); /* FIXME: yuck */ } else if (this->lhs_type == POLICY_LEX_DOUBLE_QUOTED_STRING) { if (radius_xlat(lhs_buffer, sizeof(lhs_buffer), this->lhs, state->request, NULL) > 0) { data = lhs_buffer; } } } switch (this->compare) { case POLICY_LEX_L_BRACKET: /* nested brackets are a special case */ rcode = evaluate_condition(state, this->child); break; case POLICY_LEX_L_NOT: rcode = evaluate_condition(state, this->child); rcode = (rcode == FALSE); /* reverse sense of test */ break; case POLICY_LEX_CMP_FALSE: /* non-existence */ if (this->lhs_type == POLICY_LEX_BARE_WORD) { vp = find_vp(state->request, this->lhs); rcode = (vp == NULL); } else { rcode = (data == NULL); } break; case POLICY_LEX_CMP_TRUE: /* existence */ if (this->lhs_type == POLICY_LEX_BARE_WORD) { vp = find_vp(state->request, this->lhs); rcode = (vp != NULL); } else { rcode = (data != NULL); } break; default: /* process other comparisons */ if ((this->compare != POLICY_LEX_CMP_EQUALS) &&#ifdef HAVE_REGEX_H (this->compare != POLICY_LEX_RX_EQUALS) && (this->compare != POLICY_LEX_RX_NOT_EQUALS) &&#endif (this->compare != POLICY_LEX_LT) && (this->compare != POLICY_LEX_GT) && (this->compare != POLICY_LEX_LE) && (this->compare != POLICY_LEX_GE) && (this->compare != POLICY_LEX_CMP_NOT_EQUALS)) { fprintf(stderr, "%d: bad comparison\n", this->item.lineno); return FALSE; } if (this->lhs_type == POLICY_LEX_BARE_WORD) { VALUE_PAIR *myvp; vp = find_vp(state->request, this->lhs); /* * A op B is FALSE if A doesn't * exist. */ if (!vp) { rcode = FALSE; break; } /* * FIXME: Move sanity checks to * post-parse code, so we don't do * it on every packet. */ vp_prints_value(buffer, sizeof(buffer), vp, 0); myvp = pairmake(vp->name, this->rhs, T_OP_EQ); if (!myvp) return FALSE; /* memory failure */ data = buffer; /* * FIXME: What to do about comparisons * where vp doesn't exist? Right now, * "simplepaircmp" returns -1, which is * probably a bad idea. it should
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -