/src/freeradius-server/src/lib/unlang/try.c
Line | Count | Source |
1 | | /* |
2 | | * This program is free software; you can redistribute it and/or modify |
3 | | * it under the terms of the GNU General Public License as published by |
4 | | * the Free Software Foundation; either version 2 of the License, or |
5 | | * (at your option) any later version. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
15 | | */ |
16 | | |
17 | | /** |
18 | | * $Id: 81f7daf8cbd3dd0d8f6f13399ea86d85e65a6d90 $ |
19 | | * |
20 | | * @file unlang/try.c |
21 | | * @brief Unlang "try" keyword evaluation. |
22 | | * |
23 | | * @copyright 2023 Network RADIUS SAS (legal@networkradius.com) |
24 | | */ |
25 | | RCSID("$Id: 81f7daf8cbd3dd0d8f6f13399ea86d85e65a6d90 $") |
26 | | |
27 | | #include "unlang_priv.h" |
28 | | #include "try_priv.h" |
29 | | |
30 | | static unlang_action_t skip_to_catch(UNUSED unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame) |
31 | 0 | { |
32 | 0 | rlm_rcode_t rcode = unlang_interpret_rcode(request); |
33 | 0 | unlang_try_t const *gext = unlang_generic_to_try(frame->instruction); |
34 | |
|
35 | 0 | fr_assert(frame->instruction->type == UNLANG_TYPE_TRY); |
36 | 0 | fr_assert(rcode < RLM_MODULE_NUMCODES); |
37 | | |
38 | | /* |
39 | | * Push the one "catch" section that we want to run. Once it's done, it will pop, return to us, |
40 | | * and we will continue with the next sibling. |
41 | | */ |
42 | 0 | if (gext->catch[rcode]) { |
43 | 0 | if (unlang_interpret_push(NULL, request, gext->catch[rcode], |
44 | 0 | FRAME_CONF(RLM_MODULE_NOT_SET, UNLANG_SUB_FRAME), false) < 0) { |
45 | 0 | RETURN_UNLANG_ACTION_FATAL; |
46 | 0 | } |
47 | | |
48 | 0 | return UNLANG_ACTION_PUSHED_CHILD; |
49 | 0 | } |
50 | | |
51 | 0 | RDEBUG3("No catch section for %s", |
52 | 0 | fr_table_str_by_value(mod_rcode_table, rcode, "<invalid>")); |
53 | | |
54 | | /* |
55 | | * Go to the next sibling, which MUST NOT be a "catch". |
56 | | */ |
57 | 0 | return UNLANG_ACTION_CALCULATE_RESULT; |
58 | 0 | } |
59 | | |
60 | | static unlang_action_t unlang_try(UNUSED unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame) |
61 | 0 | { |
62 | | /* |
63 | | * When this frame finishes, jump ahead to the appropriate "catch". |
64 | | * |
65 | | * All of the magic is done in the compile phase. |
66 | | */ |
67 | 0 | frame_repeat(frame, skip_to_catch); |
68 | |
|
69 | 0 | return unlang_interpret_push_children(NULL, request, RLM_MODULE_NOT_SET, UNLANG_NEXT_SIBLING); |
70 | 0 | } |
71 | | |
72 | | static unlang_t *unlang_compile_try(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_ITEM const *ci) |
73 | 0 | { |
74 | 0 | CONF_SECTION *cs = cf_item_to_section(ci); |
75 | 0 | unlang_group_t *g; |
76 | 0 | unlang_t *c; |
77 | 0 | CONF_SECTION *parent_cs, *next; |
78 | 0 | int i; |
79 | 0 | CONF_SECTION *default_catch = NULL; |
80 | 0 | unlang_compile_ctx_t unlang_ctx2; |
81 | 0 | CONF_SECTION *catcher[RLM_MODULE_NUMCODES] = {}; |
82 | | |
83 | | /* |
84 | | * The transaction is empty, ignore it. |
85 | | */ |
86 | 0 | if (!cf_item_next(cs, NULL)) { |
87 | 0 | cf_log_err(cs, "'try' sections cannot be empty"); |
88 | 0 | print_url: |
89 | 0 | cf_log_err(ci, DOC_KEYWORD_REF(try)); |
90 | 0 | return NULL; |
91 | 0 | } |
92 | | |
93 | 0 | if (cf_section_name2(cs) != NULL) { |
94 | 0 | cf_log_err(cs, "Unexpected argument to 'try' section"); |
95 | 0 | goto print_url; |
96 | 0 | } |
97 | | |
98 | 0 | parent_cs = cf_item_to_section(cf_parent(cs)); |
99 | 0 | next = cf_section_next(parent_cs, cs); |
100 | |
|
101 | 0 | if (!next || |
102 | 0 | (strcmp(cf_section_name1(next), "catch") != 0)) { |
103 | 0 | cf_log_err(cs, "'try' sections must be followed by a 'catch'"); |
104 | 0 | goto print_url; |
105 | 0 | } |
106 | | |
107 | 0 | g = unlang_group_allocate(parent, cs, UNLANG_TYPE_TRY); |
108 | 0 | if (!g) return NULL; |
109 | | |
110 | 0 | c = unlang_group_to_generic(g); |
111 | 0 | c->debug_name = c->name = cf_section_name1(cs); |
112 | | |
113 | | /* |
114 | | * Whatever the input compile ctx is, we over-ride the default actions. |
115 | | */ |
116 | 0 | unlang_compile_ctx_copy(&unlang_ctx2, unlang_ctx); |
117 | | |
118 | | /* |
119 | | * Loop over all of the "catch" sections, figuring out which rcodes are being "catch"ed. |
120 | | */ |
121 | 0 | for (default_catch = NULL; |
122 | 0 | next && (strcmp(cf_section_name1(next), "catch") == 0); |
123 | 0 | next = cf_section_next(parent_cs, next)) { |
124 | 0 | rlm_rcode_t rcode; |
125 | 0 | char const *name; |
126 | | |
127 | | /* |
128 | | * catch { ... } is the default, and we can't |
129 | | * have anything after it. |
130 | | */ |
131 | 0 | if (default_catch) { |
132 | 0 | cf_log_err(default_catch, "Invalid 'catch' - cannot have another 'catch' after a default 'catch { ... }'"); |
133 | 0 | cs = default_catch; |
134 | 0 | print_catch_url: |
135 | 0 | cf_log_err(cs, DOC_KEYWORD_REF(catch)); |
136 | 0 | talloc_free(g); |
137 | 0 | return NULL; |
138 | 0 | } |
139 | | |
140 | 0 | name = cf_section_name2(next); |
141 | 0 | if (!name) { |
142 | 0 | default_catch = next; |
143 | 0 | continue; |
144 | 0 | } |
145 | | |
146 | 0 | rcode = fr_table_value_by_str(mod_rcode_table, name, RLM_MODULE_NOT_SET); |
147 | 0 | if (rcode == RLM_MODULE_NOT_SET) { |
148 | 0 | cf_log_err(cs, "Invalid argument to 'catch' - unknown rcode '%s'.", name); |
149 | 0 | goto print_catch_url; |
150 | 0 | } |
151 | | |
152 | 0 | if (catcher[rcode]) { |
153 | 0 | cf_log_err(next, "Duplicate rcode '%s'", name); |
154 | 0 | cf_log_err(catcher[rcode], "First instance is here"); |
155 | 0 | goto print_catch_url; |
156 | 0 | } |
157 | 0 | catcher[rcode] = next; |
158 | |
|
159 | 0 | for (i = 0; (name = cf_section_argv(next, i)) != NULL; i++) { |
160 | 0 | rcode = fr_table_value_by_str(mod_rcode_table, name, RLM_MODULE_NOT_SET); |
161 | 0 | if (rcode == RLM_MODULE_NOT_SET) { |
162 | 0 | cf_log_err(cs, "Invalid argument to 'catch' - unknown rcode '%s'.", name); |
163 | 0 | goto print_catch_url; |
164 | 0 | } |
165 | | |
166 | 0 | if (catcher[rcode]) { |
167 | 0 | cf_log_err(next, "Duplicate rcode '%s'", name); |
168 | 0 | cf_log_err(catcher[rcode], "First instance is here"); |
169 | 0 | goto print_catch_url; |
170 | 0 | } |
171 | | |
172 | 0 | catcher[rcode] = next; |
173 | 0 | } |
174 | 0 | } |
175 | | |
176 | | /* |
177 | | * Check that the default will be used. |
178 | | * |
179 | | * Note that we do NOT change the priorities for the defaults. |
180 | | */ |
181 | 0 | if (default_catch) { |
182 | 0 | bool set = false; |
183 | |
|
184 | 0 | for (i = 0; i < RLM_MODULE_NUMCODES; i++) { |
185 | 0 | if (!catcher[i]) { |
186 | 0 | set = true; |
187 | 0 | break; |
188 | 0 | } |
189 | 0 | } |
190 | |
|
191 | 0 | if (!set) { |
192 | 0 | cf_log_err(default_catch, "Invalid 'catch { ... }' - all rcodes had previously been used"); |
193 | 0 | goto print_catch_url; |
194 | 0 | } |
195 | | |
196 | | /* |
197 | | * Errors are still "return", even in a default "catch". |
198 | | * |
199 | | * Normal rcodes will run to the end of the try section, and then be "catch"ed. |
200 | | */ |
201 | 0 | if (!catcher[RLM_MODULE_REJECT]) catcher[RLM_MODULE_REJECT] = default_catch; |
202 | 0 | if (!catcher[RLM_MODULE_FAIL]) catcher[RLM_MODULE_FAIL] = default_catch; |
203 | 0 | if (!catcher[RLM_MODULE_INVALID]) catcher[RLM_MODULE_INVALID] = default_catch; |
204 | 0 | if (!catcher[RLM_MODULE_DISALLOW]) catcher[RLM_MODULE_DISALLOW] = default_catch; |
205 | 0 | if (!catcher[RLM_MODULE_TIMEOUT]) catcher[RLM_MODULE_TIMEOUT] = default_catch; |
206 | 0 | } |
207 | | |
208 | | /* |
209 | | * Loop again over the rcodes, setting the child actions to RETURN if necessary. |
210 | | * |
211 | | * If the child is returning for that action, ensure that _we_ aren't returning. |
212 | | * |
213 | | * Note that as above, reject / fail / invalid / disallow / timeout are errors, and cause the |
214 | | * child to immediately return. All other rcodes |
215 | | */ |
216 | 0 | for (i = 0; i < RLM_MODULE_NUMCODES; i++) { |
217 | | /* |
218 | | * No one cares about this rcode. It can return, reject, etc. |
219 | | */ |
220 | 0 | if (!catcher[i]) continue; |
221 | | |
222 | | /* |
223 | | * Error rcodes cause the child section to bail immediately, but the "try" instruction |
224 | | * does not bail. |
225 | | */ |
226 | 0 | if ((i == RLM_MODULE_REJECT) || |
227 | 0 | (i == RLM_MODULE_FAIL) || |
228 | 0 | (i == RLM_MODULE_INVALID) || |
229 | 0 | (i == RLM_MODULE_DISALLOW) || |
230 | 0 | (i == RLM_MODULE_TIMEOUT)) { |
231 | 0 | unlang_ctx2.actions.actions[i] = MOD_ACTION_RETURN; |
232 | 0 | c->actions.actions[i] = MOD_ACTION_NOT_SET; |
233 | 0 | continue; |
234 | 0 | } |
235 | | |
236 | | /* |
237 | | * Normal rcodes cause the child section to run to completion, and the "try" section does |
238 | | * not bail. |
239 | | */ |
240 | 0 | if ((unlang_ctx2.actions.actions[i] > MOD_ACTION_NOT_SET) && |
241 | 0 | (unlang_ctx2.actions.actions[i] < MOD_PRIORITY_MIN)) { |
242 | 0 | unlang_ctx2.actions.actions[i] = MOD_ACTION_NOT_SET; |
243 | 0 | c->actions.actions[i] = MOD_ACTION_NOT_SET; |
244 | 0 | } |
245 | 0 | } |
246 | | |
247 | | /* |
248 | | * Compile the children using the new compile ctx. |
249 | | */ |
250 | 0 | return unlang_compile_children(g, &unlang_ctx2); |
251 | 0 | } |
252 | | |
253 | | |
254 | | void unlang_try_init(void) |
255 | 0 | { |
256 | 0 | unlang_register(&(unlang_op_t){ |
257 | 0 | .name = "try", |
258 | 0 | .type = UNLANG_TYPE_TRY, |
259 | 0 | .flag = UNLANG_OP_FLAG_DEBUG_BRACES | UNLANG_OP_FLAG_RCODE_SET, |
260 | |
|
261 | 0 | .compile = unlang_compile_try, |
262 | 0 | .interpret = unlang_try, |
263 | |
|
264 | 0 | .unlang_size = sizeof(unlang_try_t), |
265 | 0 | .unlang_name = "unlang_try_t", |
266 | 0 | }); |
267 | 0 | } |