From: Arran Cudbard-Bell Date: Fri, 5 Apr 2019 23:30:45 +0000 (-0400) Subject: Finish breaking keywords out into separate source files X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e52c0710136b864ed33e9526a478dd4f62da052b;p=thirdparty%2Ffreeradius-server.git Finish breaking keywords out into separate source files --- diff --git a/src/lib/unlang/all.mk b/src/lib/unlang/all.mk index ce252773633..f8a2ba956c4 100644 --- a/src/lib/unlang/all.mk +++ b/src/lib/unlang/all.mk @@ -1,15 +1,20 @@ TARGET := libfreeradius-unlang.a -SOURCES := compile.c \ +SOURCES := call.c \ + compile.c \ + condition.c \ foreach.c \ + function.c \ + group.c \ interpret.c \ io.c \ - op.c \ load_balance.c \ map.c \ module.c \ parallel.c \ + return.c \ subrequest.c \ + switch.c \ xlat.c HEADERS := $(subst src/lib/,,$(wildcard src/lib/unlang/*.h)) diff --git a/src/lib/unlang/call.c b/src/lib/unlang/call.c new file mode 100644 index 00000000000..bdef43b76f8 --- /dev/null +++ b/src/lib/unlang/call.c @@ -0,0 +1,128 @@ +/* + * 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 + */ + +/** + * $Id$ + * + * @file unlang/call.c + * @brief Unlang "call" keyword evaluation. Used for calling virtual servers. + * + * @copyright 2006-2019 The FreeRADIUS server project + */ +RCSID("$Id$") + +#include "unlang_priv.h" + +static unlang_action_t unlang_call(REQUEST *request, + UNUSED rlm_rcode_t *presult, UNUSED int *priority) +{ + unlang_stack_t *stack = request->stack; + unlang_stack_frame_t *frame = &stack->frame[stack->depth]; + unlang_t *instruction = frame->instruction; + unlang_group_t *g; + int indent; + fr_io_final_t final; + unlang_stack_t *current; + CONF_SECTION *server_cs; + + g = unlang_generic_to_group(instruction); + rad_assert(g->children != NULL); + + /* + * @todo - allow for other process functions. Mostly + * because we need to save and resume this function, and + * we haven't bothered to do that so far. + * + * If we DO allow other functions, we need to replace + * request->async->listener, as we want to pretend this + * is a virtual request which didn't come in from the + * network. i.e. the other virtual server shouldn't be + * able to access request->async->listener, and muck with + * it's statistics, see it's configuration, etc. + */ + rad_assert(request->async->process == unlang_io_process_interpret); + + /* + * @todo - We probably want to just remove the 'stack' + * parameter from the interpreter function arguments. + * It's not needed there. + */ + rad_assert(stack == request->stack); + + indent = request->log.unlang_indent; + request->log.unlang_indent = 0; /* the process function expects this */ + + current = request->stack; + request->stack = talloc_zero(request, unlang_stack_t); + + server_cs = request->server_cs; + request->server_cs = g->server_cs; + + memcpy(&request->async->process, &g->process, sizeof(request->async->process)); + + RDEBUG("server %s {", cf_section_name2(g->server_cs)); + + /* + * @todo - we can't change protocols (e.g. RADIUS -> + * DHCP) unless we're in a subrequest. + * + * @todo - we can't change packet types + * (e.g. Access-Request -> Accounting-Request) unless + * we're in a subrequest. + */ + final = request->async->process(request->async->process_inst, request, FR_IO_ACTION_RUN); + + RDEBUG("} # server %s", cf_section_name2(g->server_cs)); + + /* + * All other return codes are semantically equivalent for + * our purposes. "DONE" means "stopped without reply", + * and REPLY means "finished successfully". Neither of + * those map well into module rcodes. Instead, we rely + * on the caller to look at request->reply->code. + */ + if (final == FR_IO_YIELD) { + RDEBUG2("Noo yield for you!"); + } + + /* + * @todo - save these in a resume state somewhere... + */ + request->log.unlang_indent = indent; + request->async->process = unlang_io_process_interpret; + talloc_free(request->stack); + request->stack = current; + request->server_cs = server_cs; + + RDEBUG("Continuing with contents of %s { ...", instruction->debug_name); + + /* + * And then call the children to process the answer. + */ + unlang_push(stack, g->children, frame->result, UNLANG_NEXT_SIBLING, UNLANG_SUB_FRAME); + return UNLANG_ACTION_PUSHED_CHILD; +} + +void unlang_call_init(void) +{ + unlang_op_register(UNLANG_TYPE_CALL, + &(unlang_op_t){ + .name = "call", + .func = unlang_call, + .debug_braces = true + }); +} + diff --git a/src/lib/unlang/condition.c b/src/lib/unlang/condition.c new file mode 100644 index 00000000000..79132f05a13 --- /dev/null +++ b/src/lib/unlang/condition.c @@ -0,0 +1,107 @@ +/* + * 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 + */ + +/** + * $Id$ + * + * @file unlang/condition.c + * @brief Unlang "condition" keyword evaluation. + * + * @copyright 2006-2019 The FreeRADIUS server project + */ +RCSID("$Id$") + +#include "unlang_priv.h" +#include "group.h" + +static unlang_action_t unlang_if(REQUEST *request, + rlm_rcode_t *presult, int *priority) +{ + int condition; + unlang_stack_t *stack = request->stack; + unlang_stack_frame_t *frame = &stack->frame[stack->depth]; + unlang_t *instruction = frame->instruction; + unlang_group_t *g; + + g = unlang_generic_to_group(instruction); + rad_assert(g->cond != NULL); + + condition = cond_eval(request, *presult, 0, g->cond); + if (condition < 0) { + switch (condition) { + case -2: + REDEBUG("Condition evaluation failed because a referenced attribute " + "was not found in the request"); + break; + default: + case -1: + REDEBUG("Condition evaluation failed because the value of an operand " + "could not be determined"); + break; + } + condition = 0; + } + + /* + * Didn't pass. Remember that. + */ + if (!condition) { + RDEBUG2("..."); + + if (*presult != RLM_MODULE_UNKNOWN) *priority = instruction->actions[*presult]; + + return UNLANG_ACTION_EXECUTE_NEXT; + } + + /* + * Tell the main interpreter to skip over the else / + * elsif blocks, as this "if" condition was taken. + */ + while (frame->next && + ((frame->next->type == UNLANG_TYPE_ELSE) || + (frame->next->type == UNLANG_TYPE_ELSIF))) { + frame->next = frame->next->next; + } + + /* + * We took the "if". Go recurse into its' children. + */ + return unlang_group(request, presult, priority); +} + +void unlang_condition_init(void) +{ + unlang_op_register(UNLANG_TYPE_IF, + &(unlang_op_t){ + .name = "if", + .func = unlang_if, + .debug_braces = true + }); + + unlang_op_register(UNLANG_TYPE_ELSE, + &(unlang_op_t){ + .name = "else", + .func = unlang_group, + .debug_braces = true + }); + + unlang_op_register(UNLANG_TYPE_ELSIF, + &(unlang_op_t){ + .name = "elseif", + .func = unlang_if, + .debug_braces = true + }); +} diff --git a/src/lib/unlang/foreach.c b/src/lib/unlang/foreach.c index a110fdb1f90..9408cc32892 100644 --- a/src/lib/unlang/foreach.c +++ b/src/lib/unlang/foreach.c @@ -25,6 +25,9 @@ RCSID("$Id$") #include "unlang_priv.h" +#include "return.h" + +#define unlang_break unlang_return /** State of a foreach loop * @@ -177,4 +180,10 @@ void unlang_foreach_init(void) .func = unlang_foreach, .debug_braces = true }); + + unlang_op_register(UNLANG_TYPE_BREAK, + &(unlang_op_t){ + .name = "break", + .func = unlang_break, + }); } diff --git a/src/lib/unlang/function.c b/src/lib/unlang/function.c new file mode 100644 index 00000000000..1297fcdaf6b --- /dev/null +++ b/src/lib/unlang/function.c @@ -0,0 +1,138 @@ +/* + * 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 + */ + +/** + * $Id$ + * + * @file unlang/function.c + * @brief Unlang "function" keyword evaluation. + + * @copyright 2018 The FreeRADIUS server project + * @copyright 2018 Arran Cudbard-Bell + */ +RCSID("$Id$") + +#include "unlang_priv.h" + +/* + * Some functions differ mainly in their parsing + */ +typedef struct { + unlang_function_t func; //!< To call when going down the stack. + unlang_function_t repeat; //!< To call when going back up the stack. + void *uctx; //!< Uctx to pass to function. +} unlang_frame_state_func_t; + +/** Static instruction for allowing modules/xlats to call functions within themselves, or submodules + * + */ +static unlang_t function_instruction = { + .type = UNLANG_TYPE_FUNCTION, + .name = "function", + .debug_name = "function", + .actions = { + [RLM_MODULE_REJECT] = 0, + [RLM_MODULE_FAIL] = 0, + [RLM_MODULE_OK] = 0, + [RLM_MODULE_HANDLED] = 0, + [RLM_MODULE_INVALID] = 0, + [RLM_MODULE_USERLOCK] = 0, + [RLM_MODULE_NOTFOUND] = 0, + [RLM_MODULE_NOOP] = 0, + [RLM_MODULE_UPDATED] = 0 + } +}; + +/** Call a generic function + * + * @param[in] request The current request. + * @param[out] presult The frame result. Always set to RLM_MODULE_OK (fixme?). + * @param[out] priority of the result. + */ +static unlang_action_t unlang_function_call(REQUEST *request, + rlm_rcode_t *presult, int *priority) +{ + unlang_stack_t *stack = request->stack; + unlang_stack_frame_t *frame = &stack->frame[stack->depth]; + unlang_frame_state_func_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t); + unlang_action_t ua; + char const *caller; + + /* + * Don't let the callback mess with the current + * module permanently. + */ + caller = request->module; + request->module = NULL; + if (!frame->repeat) { + ua = state->func(request, presult, priority, state->uctx); + } else { + ua = state->repeat(request, presult, priority, state->uctx); + } + request->module = caller; + + return ua; +} + +/** Push a generic function onto the unlang stack + * + * These can be pushed by any other type of unlang op to allow a submodule or function + * deeper in the C call stack to establish a new resumption point. + * + * @param[in] request The current request. + * @param[in] func to call going up the stack. + * @param[in] repeat function to call going back down the stack (may be NULL). + * This may be the same as func. + * @param[in] uctx to pass to func. + */ +void unlang_push_function(REQUEST *request, unlang_function_t func, unlang_function_t repeat, void *uctx) +{ + unlang_stack_t *stack = request->stack; + unlang_stack_frame_t *frame; + unlang_frame_state_func_t *state; + + /* + * Push module's function + */ + unlang_push(stack, &function_instruction, RLM_MODULE_UNKNOWN, UNLANG_NEXT_STOP, false); + frame = &stack->frame[stack->depth]; + + /* + * Tell the interpreter to call unlang_function_call + * again when going back up the stack. + */ + if (repeat) frame->repeat = true; + + /* + * Allocate state + */ + MEM(frame->state = state = talloc_zero(stack, unlang_frame_state_func_t)); + + state->func = func; + state->repeat = repeat; + state->uctx = uctx; +} + +void unlang_function_init(void) +{ + unlang_op_register(UNLANG_TYPE_FUNCTION, + &(unlang_op_t){ + .name = "function", + .func = unlang_function_call, + .debug_braces = false + }); + +} diff --git a/src/lib/unlang/group.c b/src/lib/unlang/group.c new file mode 100644 index 00000000000..3ef49ae0c85 --- /dev/null +++ b/src/lib/unlang/group.c @@ -0,0 +1,70 @@ +/* + * 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 + */ + +/** + * $Id$ + * + * @file unlang/group.c + * @brief Unlang "group" keyword evaluation. + * + * @copyright 2006-2019 The FreeRADIUS server project + */ +RCSID("$Id$") + +#include "unlang_priv.h" +#include "group.h" + +#define unlang_policy unlang_group + +unlang_action_t unlang_group(REQUEST *request, + UNUSED rlm_rcode_t *result, UNUSED int *priority) +{ + unlang_stack_t *stack = request->stack; + unlang_stack_frame_t *frame = &stack->frame[stack->depth]; + unlang_t *instruction = frame->instruction; + unlang_group_t *g; + + g = unlang_generic_to_group(instruction); + + /* + * The compiler catches most of these, EXCEPT for the + * top-level 'recv Access-Request' etc. Which can exist, + * and can be empty. + */ + if (!g->children) { + RDEBUG2("} # %s ... ", instruction->debug_name); + return UNLANG_ACTION_EXECUTE_NEXT; + } + + unlang_push(stack, g->children, frame->result, UNLANG_NEXT_SIBLING, UNLANG_SUB_FRAME); + return UNLANG_ACTION_PUSHED_CHILD; +} + +void unlang_group_init(void) +{ + unlang_op_register(UNLANG_TYPE_GROUP, + &(unlang_op_t){ + .name = "group", + .func = unlang_group, + .debug_braces = true + }); + + unlang_op_register(UNLANG_TYPE_POLICY, + &(unlang_op_t){ + .name = "policy", + .func = unlang_policy, + }); +} diff --git a/src/lib/unlang/group.h b/src/lib/unlang/group.h new file mode 100644 index 00000000000..20bd72546a4 --- /dev/null +++ b/src/lib/unlang/group.h @@ -0,0 +1,40 @@ +#pragma once +/* + * 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, 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * $Id$ + * + * @file unlang/parallel.h + * @brief Private interpreter structures and functions + * + * Should be moved into parallel.c when the parallel stuff is fully extracted + * from interpret.c + * + * @copyright 2006-2019 The FreeRADIUS server project + */ +#include "unlang_priv.h" + +#ifdef __cplusplus +extern "C" { +#endif + +unlang_action_t unlang_group(REQUEST *request, + UNUSED rlm_rcode_t *result, UNUSED int *priority); + +#ifdef __cplusplus +} +#endif diff --git a/src/lib/unlang/interpret.c b/src/lib/unlang/interpret.c index 422b27688f5..e3def407fee 100644 --- a/src/lib/unlang/interpret.c +++ b/src/lib/unlang/interpret.c @@ -1663,12 +1663,22 @@ int unlang_init(void) unlang_op_register(UNLANG_TYPE_RESUME, &(unlang_op_t){ .name = "resume", .func = unlang_resume }); /* Register operations for the default keywords */ - if (unlang_op_init() < 0) return -1; + unlang_condition_init(); + unlang_foreach_init(); + unlang_function_init(); + unlang_group_init(); + unlang_load_balance_init(); + unlang_map_init(); + unlang_module_init(); + unlang_parallel_init(); + unlang_return_init(); + if (unlang_subrequest_init() < 0) return -1; + unlang_switch_init(); return 0; } void unlang_free(void) { - unlang_op_free(); + unlang_subrequest_free(); } diff --git a/src/lib/unlang/load_balance.c b/src/lib/unlang/load_balance.c index 0cca703d06e..6667b2d785b 100644 --- a/src/lib/unlang/load_balance.c +++ b/src/lib/unlang/load_balance.c @@ -17,7 +17,7 @@ /** * $Id$ * - * @file unlang/parallel.c + * @file unlang/load_balance.c * @brief Implementation of the unlang "load-balance" keyword. * * @copyright 2006-2019 The FreeRADIUS server project diff --git a/src/lib/unlang/op.c b/src/lib/unlang/op.c deleted file mode 100644 index cbf25c10831..00000000000 --- a/src/lib/unlang/op.c +++ /dev/null @@ -1,596 +0,0 @@ -/* - * 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 - */ - -/** - * $Id$ - * - * @file unlang/interpret.c - * @brief Execute compiled unlang structures using an iterative interpreter. - * - * @copyright 2006-2016 The FreeRADIUS server project - */ -RCSID("$Id$") - -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include "unlang_priv.h" - -/* - * Some functions differ mainly in their parsing - */ - -#define unlang_policy unlang_group -#define unlang_break unlang_return - - -typedef struct { - unlang_function_t func; //!< To call when going down the stack. - unlang_function_t repeat; //!< To call when going back up the stack. - void *uctx; //!< Uctx to pass to function. -} unlang_frame_state_func_t; - -/** Static instruction for allowing modules/xlats to call functions within themselves, or submodules - * - */ -static unlang_t function_instruction = { - .type = UNLANG_TYPE_FUNCTION, - .name = "function", - .debug_name = "function", - .actions = { - [RLM_MODULE_REJECT] = 0, - [RLM_MODULE_FAIL] = 0, - [RLM_MODULE_OK] = 0, - [RLM_MODULE_HANDLED] = 0, - [RLM_MODULE_INVALID] = 0, - [RLM_MODULE_USERLOCK] = 0, - [RLM_MODULE_NOTFOUND] = 0, - [RLM_MODULE_NOOP] = 0, - [RLM_MODULE_UPDATED] = 0 - }, -}; - -/** Call a generic function - * - * @param[in] request The current request. - * @param[out] presult The frame result. Always set to RLM_MODULE_OK (fixme?). - * @param[out] priority of the result. - */ -static unlang_action_t unlang_function_call(REQUEST *request, - rlm_rcode_t *presult, int *priority) -{ - unlang_stack_t *stack = request->stack; - unlang_stack_frame_t *frame = &stack->frame[stack->depth]; - unlang_frame_state_func_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t); - unlang_action_t ua; - char const *caller; - - /* - * Don't let the callback mess with the current - * module permanently. - */ - caller = request->module; - request->module = NULL; - if (!frame->repeat) { - ua = state->func(request, presult, priority, state->uctx); - } else { - ua = state->repeat(request, presult, priority, state->uctx); - } - request->module = caller; - - return ua; -} - -/** Push a generic function onto the unlang stack - * - * These can be pushed by any other type of unlang op to allow a submodule or function - * deeper in the C call stack to establish a new resumption point. - * - * @param[in] request The current request. - * @param[in] func to call going up the stack. - * @param[in] repeat function to call going back down the stack (may be NULL). - * This may be the same as func. - * @param[in] uctx to pass to func. - */ -void unlang_push_function(REQUEST *request, unlang_function_t func, unlang_function_t repeat, void *uctx) -{ - unlang_stack_t *stack = request->stack; - unlang_stack_frame_t *frame; - unlang_frame_state_func_t *state; - - /* - * Push module's function - */ - unlang_push(stack, &function_instruction, RLM_MODULE_UNKNOWN, UNLANG_NEXT_STOP, false); - frame = &stack->frame[stack->depth]; - - /* - * Tell the interpreter to call unlang_function_call - * again when going back up the stack. - */ - if (repeat) frame->repeat = true; - - /* - * Allocate state - */ - MEM(frame->state = state = talloc_zero(stack, unlang_frame_state_func_t)); - - state->func = func; - state->repeat = repeat; - state->uctx = uctx; -} - -static unlang_action_t unlang_group(REQUEST *request, - UNUSED rlm_rcode_t *result, UNUSED int *priority) -{ - unlang_stack_t *stack = request->stack; - unlang_stack_frame_t *frame = &stack->frame[stack->depth]; - unlang_t *instruction = frame->instruction; - unlang_group_t *g; - - g = unlang_generic_to_group(instruction); - - /* - * The compiler catches most of these, EXCEPT for the - * top-level 'recv Access-Request' etc. Which can exist, - * and can be empty. - */ - if (!g->children) { - RDEBUG2("} # %s ... ", instruction->debug_name); - return UNLANG_ACTION_EXECUTE_NEXT; - } - - unlang_push(stack, g->children, frame->result, UNLANG_NEXT_SIBLING, UNLANG_SUB_FRAME); - return UNLANG_ACTION_PUSHED_CHILD; -} - - -static unlang_action_t unlang_call(REQUEST *request, - UNUSED rlm_rcode_t *presult, UNUSED int *priority) -{ - unlang_stack_t *stack = request->stack; - unlang_stack_frame_t *frame = &stack->frame[stack->depth]; - unlang_t *instruction = frame->instruction; - unlang_group_t *g; - int indent; - fr_io_final_t final; - unlang_stack_t *current; - CONF_SECTION *server_cs; - - g = unlang_generic_to_group(instruction); - rad_assert(g->children != NULL); - - /* - * @todo - allow for other process functions. Mostly - * because we need to save and resume this function, and - * we haven't bothered to do that so far. - * - * If we DO allow other functions, we need to replace - * request->async->listener, as we want to pretend this - * is a virtual request which didn't come in from the - * network. i.e. the other virtual server shouldn't be - * able to access request->async->listener, and muck with - * it's statistics, see it's configuration, etc. - */ - rad_assert(request->async->process == unlang_io_process_interpret); - - /* - * @todo - We probably want to just remove the 'stack' - * parameter from the interpreter function arguments. - * It's not needed there. - */ - rad_assert(stack == request->stack); - - indent = request->log.unlang_indent; - request->log.unlang_indent = 0; /* the process function expects this */ - - current = request->stack; - request->stack = talloc_zero(request, unlang_stack_t); - - server_cs = request->server_cs; - request->server_cs = g->server_cs; - - memcpy(&request->async->process, &g->process, sizeof(request->async->process)); - - RDEBUG("server %s {", cf_section_name2(g->server_cs)); - - /* - * @todo - we can't change protocols (e.g. RADIUS -> - * DHCP) unless we're in a subrequest. - * - * @todo - we can't change packet types - * (e.g. Access-Request -> Accounting-Request) unless - * we're in a subrequest. - */ - final = request->async->process(request->async->process_inst, request, FR_IO_ACTION_RUN); - - RDEBUG("} # server %s", cf_section_name2(g->server_cs)); - - /* - * All other return codes are semantically equivalent for - * our purposes. "DONE" means "stopped without reply", - * and REPLY means "finished successfully". Neither of - * those map well into module rcodes. Instead, we rely - * on the caller to look at request->reply->code. - */ - if (final == FR_IO_YIELD) { - RDEBUG2("Noo yield for you!"); - } - - /* - * @todo - save these in a resume state somewhere... - */ - request->log.unlang_indent = indent; - request->async->process = unlang_io_process_interpret; - talloc_free(request->stack); - request->stack = current; - request->server_cs = server_cs; - - RDEBUG("Continuing with contents of %s { ...", instruction->debug_name); - - /* - * And then call the children to process the answer. - */ - unlang_push(stack, g->children, frame->result, UNLANG_NEXT_SIBLING, UNLANG_SUB_FRAME); - return UNLANG_ACTION_PUSHED_CHILD; -} - -static unlang_action_t unlang_case(REQUEST *request, - rlm_rcode_t *presult, int *priority) -{ - unlang_stack_t *stack = request->stack; - unlang_stack_frame_t *frame = &stack->frame[stack->depth]; - unlang_t *instruction = frame->instruction; - unlang_group_t *g; - - g = unlang_generic_to_group(instruction); - - if (!g->children) { - *presult = RLM_MODULE_NOOP; - *priority = 0; - return UNLANG_ACTION_CALCULATE_RESULT; - } - - return unlang_group(request, presult, priority); -} - -static unlang_action_t unlang_return(REQUEST *request, - rlm_rcode_t *presult, int *priority) -{ - int i; - VALUE_PAIR **copy_p; - unlang_stack_t *stack = request->stack; - unlang_stack_frame_t *frame = &stack->frame[stack->depth]; - unlang_t *instruction = frame->instruction; - - RDEBUG2("%s", unlang_ops[instruction->type].name); - - for (i = 8; i >= 0; i--) { - copy_p = request_data_get(request, (void *)xlat_fmt_get_vp, i); - if (copy_p) { - if (instruction->type == UNLANG_TYPE_BREAK) { - RDEBUG2("# break Foreach-Variable-%d", i); - break; - } - } - } - - frame->unwind = instruction->type; - - *presult = frame->result; - *priority = frame->priority; - - return UNLANG_ACTION_BREAK; -} - -static unlang_action_t unlang_switch(REQUEST *request, - UNUSED rlm_rcode_t *presult, UNUSED int *priority) -{ - unlang_stack_t *stack = request->stack; - unlang_stack_frame_t *frame = &stack->frame[stack->depth]; - unlang_t *instruction = frame->instruction; - unlang_t *this, *found, *null_case; - unlang_group_t *g, *h; - fr_cond_t cond; - fr_value_box_t data; - vp_map_t map; - vp_tmpl_t vpt; - - g = unlang_generic_to_group(instruction); - - memset(&cond, 0, sizeof(cond)); - memset(&map, 0, sizeof(map)); - - cond.type = COND_TYPE_MAP; - cond.data.map = ↦ - - map.op = T_OP_CMP_EQ; - map.ci = cf_section_to_item(g->cs); - - rad_assert(g->vpt != NULL); - - null_case = found = NULL; - data.datum.ptr = NULL; - - /* - * The attribute doesn't exist. We can skip - * directly to the default 'case' statement. - */ - if ((g->vpt->type == TMPL_TYPE_ATTR) && (tmpl_find_vp(NULL, request, g->vpt) < 0)) { - find_null_case: - for (this = g->children; this; this = this->next) { - rad_assert(this->type == UNLANG_TYPE_CASE); - - h = unlang_generic_to_group(this); - if (h->vpt) continue; - - found = this; - break; - } - - goto do_null_case; - } - - /* - * Expand the template if necessary, so that it - * is evaluated once instead of for each 'case' - * statement. - */ - if ((g->vpt->type == TMPL_TYPE_XLAT_STRUCT) || - (g->vpt->type == TMPL_TYPE_XLAT) || - (g->vpt->type == TMPL_TYPE_EXEC)) { - char *p; - ssize_t len; - - len = tmpl_aexpand(request, &p, request, g->vpt, NULL, NULL); - if (len < 0) goto find_null_case; - data.vb_strvalue = p; - tmpl_init(&vpt, TMPL_TYPE_UNPARSED, data.vb_strvalue, len, T_SINGLE_QUOTED_STRING); - } - - /* - * Find either the exact matching name, or the - * "case {...}" statement. - */ - for (this = g->children; this; this = this->next) { - rad_assert(this->type == UNLANG_TYPE_CASE); - - h = unlang_generic_to_group(this); - - /* - * Remember the default case - */ - if (!h->vpt) { - if (!null_case) null_case = this; - continue; - } - - /* - * If we're switching over an attribute - * AND we haven't pre-parsed the data for - * the case statement, then cast the data - * to the type of the attribute. - */ - if ((g->vpt->type == TMPL_TYPE_ATTR) && - (h->vpt->type != TMPL_TYPE_DATA)) { - map.rhs = g->vpt; - map.lhs = h->vpt; - cond.cast = g->vpt->tmpl_da; - - /* - * Remove unnecessary casting. - */ - if ((h->vpt->type == TMPL_TYPE_ATTR) && - (g->vpt->tmpl_da->type == h->vpt->tmpl_da->type)) { - cond.cast = NULL; - } - - /* - * Use the pre-expanded string. - */ - } else if ((g->vpt->type == TMPL_TYPE_XLAT_STRUCT) || - (g->vpt->type == TMPL_TYPE_XLAT) || - (g->vpt->type == TMPL_TYPE_EXEC)) { - map.rhs = h->vpt; - map.lhs = &vpt; - cond.cast = NULL; - - /* - * Else evaluate the 'switch' statement. - */ - } else { - map.rhs = h->vpt; - map.lhs = g->vpt; - cond.cast = NULL; - } - - if (cond_eval_map(request, RLM_MODULE_UNKNOWN, 0, - &cond) == 1) { - found = this; - break; - } - } - - if (!found) found = null_case; - -do_null_case: - talloc_free(data.datum.ptr); - - /* - * Nothing found. Just continue, and ignore the "switch" - * statement. - */ - if (!found) return UNLANG_ACTION_EXECUTE_NEXT; - - unlang_push(stack, found, frame->result, UNLANG_NEXT_STOP, UNLANG_SUB_FRAME); - return UNLANG_ACTION_PUSHED_CHILD; -} - -static unlang_action_t unlang_if(REQUEST *request, - rlm_rcode_t *presult, int *priority) -{ - int condition; - unlang_stack_t *stack = request->stack; - unlang_stack_frame_t *frame = &stack->frame[stack->depth]; - unlang_t *instruction = frame->instruction; - unlang_group_t *g; - - g = unlang_generic_to_group(instruction); - rad_assert(g->cond != NULL); - - condition = cond_eval(request, *presult, 0, g->cond); - if (condition < 0) { - switch (condition) { - case -2: - REDEBUG("Condition evaluation failed because a referenced attribute " - "was not found in the request"); - break; - default: - case -1: - REDEBUG("Condition evaluation failed because the value of an operand " - "could not be determined"); - break; - } - condition = 0; - } - - /* - * Didn't pass. Remember that. - */ - if (!condition) { - RDEBUG2("..."); - - if (*presult != RLM_MODULE_UNKNOWN) *priority = instruction->actions[*presult]; - - return UNLANG_ACTION_EXECUTE_NEXT; - } - - /* - * Tell the main interpreter to skip over the else / - * elsif blocks, as this "if" condition was taken. - */ - while (frame->next && - ((frame->next->type == UNLANG_TYPE_ELSE) || - (frame->next->type == UNLANG_TYPE_ELSIF))) { - frame->next = frame->next->next; - } - - /* - * We took the "if". Go recurse into its' children. - */ - return unlang_group(request, presult, priority); -} - -int unlang_op_init(void) -{ - unlang_op_register(UNLANG_TYPE_FUNCTION, - &(unlang_op_t){ - .name = "function", - .func = unlang_function_call, - .debug_braces = false - }); - - unlang_op_register(UNLANG_TYPE_GROUP, - &(unlang_op_t){ - .name = "group", - .func = unlang_group, - .debug_braces = true - }); - -#ifdef WITH_UNLANG - unlang_op_register(UNLANG_TYPE_IF, - &(unlang_op_t){ - .name = "if", - .func = unlang_if, - .debug_braces = true - }); - - unlang_op_register(UNLANG_TYPE_ELSE, - &(unlang_op_t){ - .name = "else", - .func = unlang_group, - .debug_braces = true - }); - - unlang_op_register(UNLANG_TYPE_ELSIF, - &(unlang_op_t){ - .name = "elseif", - .func = unlang_if, - .debug_braces = true - }); - - unlang_op_register(UNLANG_TYPE_SWITCH, - &(unlang_op_t){ - .name = "switch", - .func = unlang_switch, - .debug_braces = true - }); - - unlang_op_register(UNLANG_TYPE_CASE, - &(unlang_op_t){ - .name = "case", - .func = unlang_case, - .debug_braces = true - }); - - unlang_op_register(UNLANG_TYPE_BREAK, - &(unlang_op_t){ - .name = "break", - .func = unlang_break, - }); - - unlang_op_register(UNLANG_TYPE_RETURN, - &(unlang_op_t){ - .name = "return", - .func = unlang_return, - }); - - unlang_op_register(UNLANG_TYPE_POLICY, - &(unlang_op_t){ - .name = "policy", - .func = unlang_policy, - }); - - unlang_op_register(UNLANG_TYPE_CALL, - &(unlang_op_t){ - .name = "call", - .func = unlang_call, - .debug_braces = true - }); - - unlang_foreach_init(); - unlang_load_balance_init(); - unlang_map_init(); - unlang_module_init(); - unlang_parallel_init(); - if (unlang_subrequest_init() < 0) return -1; -#endif - - return 0; -} - -void unlang_op_free(void) -{ - unlang_subrequest_free(); -} diff --git a/src/lib/unlang/return.c b/src/lib/unlang/return.c new file mode 100644 index 00000000000..870b7d905f1 --- /dev/null +++ b/src/lib/unlang/return.c @@ -0,0 +1,65 @@ +/* + * 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 + */ + +/** + * $Id$ + * + * @file unlang/return.c + * @brief Unlang "return" keyword evaluation. + * + * @copyright 2006-2019 The FreeRADIUS server project + */ +RCSID("$Id$") + +#include "unlang_priv.h" +#include "return.h" + +unlang_action_t unlang_return(REQUEST *request, rlm_rcode_t *presult, int *priority) +{ + int i; + VALUE_PAIR **copy_p; + unlang_stack_t *stack = request->stack; + unlang_stack_frame_t *frame = &stack->frame[stack->depth]; + unlang_t *instruction = frame->instruction; + + RDEBUG2("%s", unlang_ops[instruction->type].name); + + for (i = 8; i >= 0; i--) { + copy_p = request_data_get(request, (void *)xlat_fmt_get_vp, i); + if (copy_p) { + if (instruction->type == UNLANG_TYPE_BREAK) { + RDEBUG2("# break Foreach-Variable-%d", i); + break; + } + } + } + + frame->unwind = instruction->type; + + *presult = frame->result; + *priority = frame->priority; + + return UNLANG_ACTION_BREAK; +} + +void unlang_return_init(void) +{ + unlang_op_register(UNLANG_TYPE_RETURN, + &(unlang_op_t){ + .name = "return", + .func = unlang_return, + }); +} diff --git a/src/lib/unlang/return.h b/src/lib/unlang/return.h new file mode 100644 index 00000000000..a52b62a6431 --- /dev/null +++ b/src/lib/unlang/return.h @@ -0,0 +1,37 @@ +#pragma once +/* + * 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, 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * $Id$ + * + * @file unlang/return.h + * @brief Declarations for the "return" keyword, used to implement other keywords + * + * @copyright 2006-2019 The FreeRADIUS server project + */ +#include "unlang_priv.h" + +#ifdef __cplusplus +extern "C" { +#endif + +unlang_action_t unlang_return(REQUEST *request, + UNUSED rlm_rcode_t *result, UNUSED int *priority); + +#ifdef __cplusplus +} +#endif diff --git a/src/lib/unlang/switch.c b/src/lib/unlang/switch.c new file mode 100644 index 00000000000..20aee1448de --- /dev/null +++ b/src/lib/unlang/switch.c @@ -0,0 +1,208 @@ +/* + * 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 + */ + +/** + * $Id$ + * + * @file unlang/switch.c + * @brief Unlang "switch" keyword evaluation. + * + * @copyright 2006-2019 The FreeRADIUS server project + */ +RCSID("$Id$") + +#include "unlang_priv.h" +#include "group.h" + +static unlang_action_t unlang_switch(REQUEST *request, + UNUSED rlm_rcode_t *presult, UNUSED int *priority) +{ + unlang_stack_t *stack = request->stack; + unlang_stack_frame_t *frame = &stack->frame[stack->depth]; + unlang_t *instruction = frame->instruction; + unlang_t *this, *found, *null_case; + unlang_group_t *g, *h; + fr_cond_t cond; + fr_value_box_t data; + vp_map_t map; + vp_tmpl_t vpt; + + g = unlang_generic_to_group(instruction); + + memset(&cond, 0, sizeof(cond)); + memset(&map, 0, sizeof(map)); + + cond.type = COND_TYPE_MAP; + cond.data.map = ↦ + + map.op = T_OP_CMP_EQ; + map.ci = cf_section_to_item(g->cs); + + rad_assert(g->vpt != NULL); + + null_case = found = NULL; + data.datum.ptr = NULL; + + /* + * The attribute doesn't exist. We can skip + * directly to the default 'case' statement. + */ + if ((g->vpt->type == TMPL_TYPE_ATTR) && (tmpl_find_vp(NULL, request, g->vpt) < 0)) { + find_null_case: + for (this = g->children; this; this = this->next) { + rad_assert(this->type == UNLANG_TYPE_CASE); + + h = unlang_generic_to_group(this); + if (h->vpt) continue; + + found = this; + break; + } + + goto do_null_case; + } + + /* + * Expand the template if necessary, so that it + * is evaluated once instead of for each 'case' + * statement. + */ + if ((g->vpt->type == TMPL_TYPE_XLAT_STRUCT) || + (g->vpt->type == TMPL_TYPE_XLAT) || + (g->vpt->type == TMPL_TYPE_EXEC)) { + char *p; + ssize_t len; + + len = tmpl_aexpand(request, &p, request, g->vpt, NULL, NULL); + if (len < 0) goto find_null_case; + data.vb_strvalue = p; + tmpl_init(&vpt, TMPL_TYPE_UNPARSED, data.vb_strvalue, len, T_SINGLE_QUOTED_STRING); + } + + /* + * Find either the exact matching name, or the + * "case {...}" statement. + */ + for (this = g->children; this; this = this->next) { + rad_assert(this->type == UNLANG_TYPE_CASE); + + h = unlang_generic_to_group(this); + + /* + * Remember the default case + */ + if (!h->vpt) { + if (!null_case) null_case = this; + continue; + } + + /* + * If we're switching over an attribute + * AND we haven't pre-parsed the data for + * the case statement, then cast the data + * to the type of the attribute. + */ + if ((g->vpt->type == TMPL_TYPE_ATTR) && + (h->vpt->type != TMPL_TYPE_DATA)) { + map.rhs = g->vpt; + map.lhs = h->vpt; + cond.cast = g->vpt->tmpl_da; + + /* + * Remove unnecessary casting. + */ + if ((h->vpt->type == TMPL_TYPE_ATTR) && + (g->vpt->tmpl_da->type == h->vpt->tmpl_da->type)) { + cond.cast = NULL; + } + + /* + * Use the pre-expanded string. + */ + } else if ((g->vpt->type == TMPL_TYPE_XLAT_STRUCT) || + (g->vpt->type == TMPL_TYPE_XLAT) || + (g->vpt->type == TMPL_TYPE_EXEC)) { + map.rhs = h->vpt; + map.lhs = &vpt; + cond.cast = NULL; + + /* + * Else evaluate the 'switch' statement. + */ + } else { + map.rhs = h->vpt; + map.lhs = g->vpt; + cond.cast = NULL; + } + + if (cond_eval_map(request, RLM_MODULE_UNKNOWN, 0, + &cond) == 1) { + found = this; + break; + } + } + + if (!found) found = null_case; + +do_null_case: + talloc_free(data.datum.ptr); + + /* + * Nothing found. Just continue, and ignore the "switch" + * statement. + */ + if (!found) return UNLANG_ACTION_EXECUTE_NEXT; + + unlang_push(stack, found, frame->result, UNLANG_NEXT_STOP, UNLANG_SUB_FRAME); + return UNLANG_ACTION_PUSHED_CHILD; +} + + +static unlang_action_t unlang_case(REQUEST *request, + rlm_rcode_t *presult, int *priority) +{ + unlang_stack_t *stack = request->stack; + unlang_stack_frame_t *frame = &stack->frame[stack->depth]; + unlang_t *instruction = frame->instruction; + unlang_group_t *g; + + g = unlang_generic_to_group(instruction); + + if (!g->children) { + *presult = RLM_MODULE_NOOP; + *priority = 0; + return UNLANG_ACTION_CALCULATE_RESULT; + } + + return unlang_group(request, presult, priority); +} + +void unlang_switch_init(void) +{ + unlang_op_register(UNLANG_TYPE_SWITCH, + &(unlang_op_t){ + .name = "switch", + .func = unlang_switch, + .debug_braces = true + }); + + unlang_op_register(UNLANG_TYPE_CASE, + &(unlang_op_t){ + .name = "case", + .func = unlang_case, + .debug_braces = true + }); +} diff --git a/src/lib/unlang/unlang_priv.h b/src/lib/unlang/unlang_priv.h index 1c31c7599e5..6da0f806074 100644 --- a/src/lib/unlang/unlang_priv.h +++ b/src/lib/unlang/unlang_priv.h @@ -391,19 +391,31 @@ REQUEST *unlang_io_child_alloc(REQUEST *parent, unlang_t *instruction, rlm_rcod * * @{ */ +void unlang_call_init(void); + +void unlang_condition_init(void); + void unlang_foreach_init(void); +void unlang_function_init(void); + +void unlang_group_init(void); + void unlang_load_balance_init(void); void unlang_map_init(void); void unlang_module_init(void); +void unlang_return_init(void); + void unlang_parallel_init(void); int unlang_subrequest_init(void); void unlang_subrequest_free(void); + +void unlang_switch_init(void); /* @} **/ #ifdef __cplusplus