The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_rest.c
Go to the documentation of this file.
1/*
2 * This program is 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 (at
5 * 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: 2fc59b55bc5d2395ab1042d2b6f69ed37ec36a98 $
19 * @file rlm_rest.c
20 * @brief Integrate FreeRADIUS with RESTfull APIs
21 *
22 * @copyright 2012-2019,2024 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23 */
24RCSID("$Id: 2fc59b55bc5d2395ab1042d2b6f69ed37ec36a98 $")
25
26#include <freeradius-devel/curl/base.h>
27#include <freeradius-devel/curl/xlat.h>
28#include <freeradius-devel/server/base.h>
29#include <freeradius-devel/server/cf_parse.h>
30
31#include <freeradius-devel/server/global_lib.h>
32#include <freeradius-devel/server/tmpl.h>
33#include <freeradius-devel/util/atexit.h>
34#include <freeradius-devel/util/debug.h>
35#include <freeradius-devel/util/uri.h>
36#include <freeradius-devel/unlang/call_env.h>
37#include <freeradius-devel/unlang/xlat_func.h>
38
39#include "rest.h"
40
41static int rest_uri_part_escape(fr_value_box_t *vb, void *uctx);
42static void *rest_uri_part_escape_uctx_alloc(UNUSED request_t *request, void const *uctx);
43
44static fr_uri_part_t const rest_uri_parts[] = {
45 { .name = "scheme", .safe_for = CURL_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L(":")), .part_adv = { [':'] = 1 }, .extra_skip = 2 },
46 { .name = "host", .safe_for = CURL_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L(":"), L("/")), .part_adv = { [':'] = 1, ['/'] = 2 }, .func = rest_uri_part_escape },
47 { .name = "port", .safe_for = CURL_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L("/")), .part_adv = { ['/'] = 1 } },
48 { .name = "method", .safe_for = CURL_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1 }, .func = rest_uri_part_escape },
49 { .name = "param", .safe_for = CURL_URI_SAFE_FOR, .func = rest_uri_part_escape },
51};
52
54
55 { L("1.0"), CURL_HTTP_VERSION_1_0 }, //!< Enforce HTTP 1.0 requests.
56 { L("1.1"), CURL_HTTP_VERSION_1_1 }, //!< Enforce HTTP 1.1 requests.
57/*
58 * These are all enum values
59 */
60#if CURL_AT_LEAST_VERSION(7,49,0)
61 { L("2.0"), CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE }, //!< Enforce HTTP 2.0 requests.
62#endif
63#if CURL_AT_LEAST_VERSION(7,33,0)
64 { L("2.0+auto"), CURL_HTTP_VERSION_2_0 }, //!< Attempt HTTP 2 requests. libcurl will fall back
65 ///< to HTTP 1.1 if HTTP 2 can't be negotiated with the
66 ///< server. (Added in 7.33.0)
67#endif
68#if CURL_AT_LEAST_VERSION(7,47,0)
69 { L("2.0+tls"), CURL_HTTP_VERSION_2TLS }, //!< Attempt HTTP 2 over TLS (HTTPS) only.
70 ///< libcurl will fall back to HTTP 1.1 if HTTP 2
71 ///< can't be negotiated with the HTTPS server.
72 ///< For clear text HTTP servers, libcurl will use 1.1.
73#endif
74 { L("default"), CURL_HTTP_VERSION_NONE } //!< We don't care about what version the library uses.
75 ///< libcurl will use whatever it thinks fit.
76};
78
79/** Unique pointer used to determine if we should explicitly disable proxying
80 *
81 */
82char const *rest_no_proxy = "*";
83
84static int rest_proxy_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent,
85 CONF_ITEM *ci, UNUSED conf_parser_t const *rule)
86{
87 static fr_table_num_sorted_t const disable_proxy_table[] = {
88 { L("no"), 1 },
89 { L("false"), 1 },
90 { L("none"), 1 }
91 };
92 static size_t disable_proxy_table_len = NUM_ELEMENTS(disable_proxy_table);
93 char const *value = cf_pair_value(cf_item_to_pair(ci));
94
95 if (fr_table_value_by_str(disable_proxy_table, value, 0) == 1) {
96 *((char const **)out) = rest_no_proxy;
97 } else {
98 *((char const **)out) = value;
99 }
100 return 0;
101}
102
103#define SECTION_REQUEST_COMMON \
104 { FR_CONF_OFFSET("body", rlm_rest_section_request_t, body_str), .dflt = "none" }, \
105 /* User authentication */ \
106 { FR_CONF_OFFSET_IS_SET("auth", FR_TYPE_VOID, 0, rlm_rest_section_request_t, auth), \
107 .func = cf_table_parse_int, .uctx = &(cf_table_parse_ctx_t){ .table = http_auth_table, .len = &http_auth_table_len }, .dflt = "none" }, \
108 { FR_CONF_OFFSET("require_auth", rlm_rest_section_request_t, require_auth), .dflt = "no" }, \
109 { FR_CONF_OFFSET("chunk", rlm_rest_section_request_t, chunk), .dflt = "0" } \
110
117
119 { FR_CONF_OFFSET("force_to", rlm_rest_section_response_t, force_to_str) }, \
120 { FR_CONF_OFFSET_TYPE_FLAGS("max_body_in", FR_TYPE_SIZE, 0, rlm_rest_section_response_t, max_body_in), .dflt = "16k" },
122};
123
127
128 /* Transfer configuration */
129 { FR_CONF_OFFSET("timeout", rlm_rest_section_t, timeout), .dflt = "4.0" },
130
131 /* TLS Parameters */
134};
135
140
141static const conf_parser_t xlat_config[] = {
144
145 /* Transfer configuration */
146 { FR_CONF_OFFSET("timeout", rlm_rest_section_t, timeout), .dflt = "4.0" },
147
148 /* TLS Parameters */
151};
152
153static const conf_parser_t module_config[] = {
154 { FR_CONF_DEPRECATED("connect_timeout", rlm_rest_t, connect_timeout) },
155 { FR_CONF_OFFSET("connect_proxy", rlm_rest_t, connect_proxy), .func = rest_proxy_parse },
156 { FR_CONF_OFFSET("http_negotiation", rlm_rest_t, http_negotiation),
157 .func = cf_table_parse_int,
159 .dflt = "default" },
160
161 { FR_CONF_OFFSET_SUBSECTION("connection", 0, rlm_rest_t, conn_config, fr_curl_conn_config) },
162
163#ifdef CURLPIPE_MULTIPLEX
164 { FR_CONF_OFFSET("multiplex", rlm_rest_t, multiplex), .dflt = "yes" },
165#endif
166
167#ifndef NDEBUG
168 { FR_CONF_OFFSET("fail_header_decode", rlm_rest_t, fail_header_decode), .dflt = "no" },
169 { FR_CONF_OFFSET("fail_body_decode", rlm_rest_t, fail_body_decode), .dflt = "no" },
170#endif
171
173};
174
175#define REST_CALL_ENV_REQUEST_COMMON(_dflt_username, _dflt_password) \
176 { FR_CALL_ENV_OFFSET("header", FR_TYPE_STRING, CALL_ENV_FLAG_MULTI, rlm_rest_call_env_t, request.header) }, \
177 { FR_CALL_ENV_OFFSET("data", FR_TYPE_STRING, CALL_ENV_FLAG_CONCAT, rlm_rest_call_env_t, request.data) }, \
178 { FR_CALL_ENV_OFFSET("username", FR_TYPE_STRING, CALL_ENV_FLAG_SINGLE | CALL_ENV_FLAG_NULLABLE | CALL_ENV_FLAG_BARE_WORD_ATTRIBUTE, \
179 rlm_rest_call_env_t, request.username), .pair.dflt_quote = T_BARE_WORD, _dflt_username }, \
180 { FR_CALL_ENV_OFFSET("password", FR_TYPE_STRING, CALL_ENV_FLAG_SINGLE | CALL_ENV_FLAG_NULLABLE | CALL_ENV_FLAG_SECRET | CALL_ENV_FLAG_BARE_WORD_ATTRIBUTE, \
181 rlm_rest_call_env_t, request.password), .pair.dflt_quote = T_BARE_WORD, _dflt_password }, \
182
183#define REST_CALL_ENV_RESPONSE_COMMON \
184 { FR_CALL_ENV_PARSE_ONLY_OFFSET("header", FR_TYPE_STRING, CALL_ENV_FLAG_ATTRIBUTE, rlm_rest_call_env_t, response.header) }, \
185
186#define REST_CALL_ENV_SECTION(_var, _dflt_username, _dflt_password) \
187static const call_env_parser_t _var[] = { \
188 { FR_CALL_ENV_SUBSECTION("request", NULL, CALL_ENV_FLAG_REQUIRED, \
189 ((call_env_parser_t[]) { \
190 { FR_CALL_ENV_OFFSET("uri", FR_TYPE_STRING, CALL_ENV_FLAG_REQUIRED | CALL_ENV_FLAG_CONCAT, rlm_rest_call_env_t, request.uri), \
191 .pair.escape = { \
192 .box_escape = { \
193 .func = fr_uri_escape, \
194 .safe_for = CURL_URI_SAFE_FOR, \
195 .always_escape = true, /* required! */ \
196 }, \
197 .mode = TMPL_ESCAPE_PRE_CONCAT, \
198 .uctx = { \
199 .func = { \
200 .alloc = rest_uri_part_escape_uctx_alloc, \
201 .uctx = rest_uri_parts \
202 }, \
203 .type = TMPL_ESCAPE_UCTX_ALLOC_FUNC \
204 }, \
205 }, \
206 .pair.literals_safe_for = CURL_URI_SAFE_FOR}, /* Do not concat */ \
207 REST_CALL_ENV_REQUEST_COMMON(_dflt_username, _dflt_password) \
208 CALL_ENV_TERMINATOR \
209 })) }, \
210 { FR_CALL_ENV_SUBSECTION("response", NULL, CALL_ENV_FLAG_NONE, \
211 ((call_env_parser_t[]) { \
212 REST_CALL_ENV_RESPONSE_COMMON \
213 CALL_ENV_TERMINATOR \
214 })) }, \
215 CALL_ENV_TERMINATOR \
216};
217
218REST_CALL_ENV_SECTION(rest_section_common_env,,)
219REST_CALL_ENV_SECTION(rest_section_authenticate_env, .pair.dflt = "User-Name", .pair.dflt = "User-Password")
220
221/*
222 * xlat call env doesn't have the same set of config items as the other sections
223 * because some values come from the xlat call itself.
224 */
226 FR_CALL_ENV_METHOD_OUT(rlm_rest_call_env_t), \
227 .env = (call_env_parser_t[]){ \
229 ((call_env_parser_t[]) { \
230 { FR_CALL_ENV_SUBSECTION("request", NULL, CALL_ENV_FLAG_NONE, \
231 ((call_env_parser_t[]) { \
232 REST_CALL_ENV_REQUEST_COMMON(,) \
234 })) }, \
235 { FR_CALL_ENV_SUBSECTION("response", NULL, CALL_ENV_FLAG_NONE, \
236 ((call_env_parser_t[]) { \
237 REST_CALL_ENV_RESPONSE_COMMON \
238 CALL_ENV_TERMINATOR \
239 })) }, \
241 }) \
242 ) }, \
244 } \
245};
246
248
251 { .out = &dict_freeradius, .proto = "freeradius" },
252 { NULL }
253};
254
258
261 { .out = &attr_rest_http_body, .name = "REST-HTTP-Body", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
262 { .out = &attr_rest_http_header, .name = "REST-HTTP-Header", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
263 { .out = &attr_rest_http_status_code, .name = "REST-HTTP-Status-Code", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
264 { NULL }
265};
266
267extern global_lib_autoinst_t const * const rlm_rest_lib[];
272
273static int8_t rest_section_cmp(void const *one, void const *two)
274{
275 rlm_rest_section_conf_t const *a = one, *b = two;
276 return CMP(a->cs, b->cs);
277}
278
279/** Update the status attribute
280 *
281 * @param[in] request The current request.
282 * @param[in] handle rest handle.
283 * @return
284 * - 0 if status was updated successfully.
285 * - -1 if status was not updated successfully.
286 */
287static int rlm_rest_status_update(request_t *request, void *handle)
288{
289 int code;
290 fr_pair_t *vp;
291
292 RDEBUG2("Updating result attribute(s)");
293
294 RINDENT();
295 code = rest_get_handle_code(handle);
296 if (!code) {
298 RDEBUG2("request.REST-HTTP-Status-Code !* ANY");
299 REXDENT();
300 return -1;
301 }
302
303 RDEBUG2("request.REST-HTTP-Status-Code := %i", code);
304
306 vp->vp_uint32 = code;
307 REXDENT();
308
309 return 0;
310}
311
313{
314 return talloc_free(uctx);
315}
316
317/** Allocate an escape uctx to pass to fr_uri_escape
318 *
319 * @param[in] request UNUSED.
320 * @param[in] uctx pointer to the start of the uri_parts array.
321 * @return A new fr_uri_escape_ctx_t.
322 */
323static void *rest_uri_part_escape_uctx_alloc(UNUSED request_t *request, void const *uctx)
324{
325 static _Thread_local fr_uri_escape_ctx_t *t_ctx;
326
327 if (unlikely(t_ctx == NULL)) {
329
330 MEM(ctx = talloc_zero(NULL, fr_uri_escape_ctx_t));
332 } else {
333 memset(t_ctx, 0, sizeof(*t_ctx));
334 }
335 t_ctx->uri_part = uctx;
336 return t_ctx;
337}
338
339/** URL escape a single box forming part of a URL
340 *
341 * @param[in] vb to escape
342 * @param[in] uctx UNUSED context containing CURL handle
343 * @return
344 * - 0 on success
345 * - -1 on failure
346 */
347static int rest_uri_part_escape(fr_value_box_t *vb, UNUSED void *uctx)
348{
349 char *escaped, *str;
350
351 escaped = curl_easy_escape(fr_curl_tmp_handle(), vb->vb_strvalue, vb->vb_length);
352 if (!escaped) return -1;
353
354 /*
355 * Returned string the same length - nothing changed
356 */
357 if (strlen(escaped) == vb->vb_length) {
358 curl_free(escaped);
359 return 0;
360 }
361
362 str = talloc_typed_strdup(vb, escaped);
363 fr_value_box_strdup_shallow_replace(vb, str, strlen(str));
364
365 curl_free(escaped);
366
367 return 0;
368}
369
370static int rlm_rest_perform(module_ctx_t const *mctx,
371 rlm_rest_section_t const *section, fr_curl_io_request_t *randle,
372 request_t *request)
373{
374 rlm_rest_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_rest_thread_t);
375 rlm_rest_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
376 int ret;
377
378 RDEBUG2("Sending HTTP %s to \"%pV\"",
379 fr_table_str_by_value(http_method_table, section->request.method, NULL), call_env->request.uri);
380
381 /*
382 * Configure various CURL options, and initialise the read/write
383 * context data.
384 */
385 ret = rest_request_config(mctx, section, request, randle, section->request.method, section->request.body,
386 call_env->request.uri->vb_strvalue,
387 call_env->request.data ? call_env->request.data->vb_strvalue : NULL);
388 if (ret < 0) return -1;
389
390 /*
391 * Send the CURL request, pre-parse headers, aggregate incoming
392 * HTTP body data into a single contiguous buffer.
393 */
394 ret = fr_curl_io_request_enqueue(t->mhandle, request, randle);
395 if (ret < 0) return -1;
396
397 return 0;
398}
399
401 xlat_ctx_t const *xctx,
402 request_t *request, UNUSED fr_value_box_list_t *in)
403{
404 rlm_rest_xlat_rctx_t *rctx = talloc_get_type_abort(xctx->rctx, rlm_rest_xlat_rctx_t);
405 int hcode;
406 ssize_t len;
407 char const *body;
409
410 fr_curl_io_request_t *handle = talloc_get_type_abort(rctx->handle, fr_curl_io_request_t);
411 rlm_rest_section_t *section = &rctx->section;
412
413 if (section->tls.extract_cert_attrs) fr_curl_response_certinfo(request, handle);
414
415 if (rlm_rest_status_update(request, handle) < 0) {
416 xa = XLAT_ACTION_FAIL;
417 goto finish;
418 }
419
420 hcode = rest_get_handle_code(handle);
421 switch (hcode) {
422 case 404:
423 case 410:
424 case 403:
425 case 401:
426 {
427 fr_pair_t *vp;
428 xa = XLAT_ACTION_FAIL;
429error:
430 rest_response_error(request, handle);
431
432 /*
433 * When the HTTP status code is a failure, put the
434 * response body in REST-HTTP-Body.
435 */
436 len = rest_get_handle_data(&body, handle);
437 if (len == 0) goto finish;
439 fr_pair_value_bstrndup(vp, body, len, true);
440 goto finish;
441 }
442 case 204:
443 goto finish;
444
445 default:
446 /*
447 * Attempt to parse content if there was any.
448 */
449 if ((hcode >= 200) && (hcode < 300)) {
450 break;
451 } else if (hcode < 500) {
452 xa = XLAT_ACTION_FAIL;
453 goto error;
454 } else {
455 xa = XLAT_ACTION_FAIL;
456 goto error;
457 }
458 }
459
460 /*
461 * Output the xlat data if the HTTP status code is one of the "success" ones.
462 *
463 * The user can check REST-HTTP-Status-Code to figure out what happened.
464 *
465 * Eventually we should just emit two boxes, one with the response code
466 * and one with the body.
467 */
468 len = rest_get_handle_data(&body, handle);
469 if (len > 0) {
470 fr_value_box_t *vb;
471
472 MEM(vb = fr_value_box_alloc_null(ctx));
473 fr_value_box_bstrndup(vb, vb, NULL, body, len, true);
475 }
476finish:
477
478 rest_slab_release(handle);
479
480 talloc_free(rctx);
481
482 return xa;
483}
484
486 { .required = true, .single = true, .type = FR_TYPE_STRING }, /* HTTP Method */
487 { .required = true, .safe_for = CURL_URI_SAFE_FOR, .type = FR_TYPE_STRING, .will_escape = true }, /* URL */
488 { .concat = true, .type = FR_TYPE_STRING }, /* Data */
489 { .type = FR_TYPE_STRING }, /* Headers */
491};
492
493/** Simple xlat to read text data from a URL
494 *
495 * Example:
496@verbatim
497%rest(POST, http://example.com/, "{ \"key\": \"value\" }", [<headers>])
498@endverbatim
499 *
500 * @ingroup xlat_functions
501 */
503 xlat_ctx_t const *xctx, request_t *request,
504 fr_value_box_list_t *in)
505{
507 rlm_rest_thread_t *t = talloc_get_type_abort(xctx->mctx->thread, rlm_rest_thread_t);
508
509 fr_curl_io_request_t *randle = NULL;
510 int ret;
511 http_method_t method;
512
513 fr_value_box_t *method_vb;
514 fr_value_box_t *uri_vb;
515 fr_value_box_t *data_vb;
516 fr_value_box_t *header_vb;
517
518 /* There are no configurable parameters other than the URI */
520 rlm_rest_section_t *section;
521
522 XLAT_ARGS(in, &method_vb, &uri_vb, &data_vb, &header_vb);
523
524 MEM(rctx = talloc(request, rlm_rest_xlat_rctx_t));
525 section = &rctx->section;
526
527 /*
528 * Section gets modified, so we need our own copy.
529 */
530 memcpy(&rctx->section, &inst->xlat, sizeof(*section));
531
532 /*
533 * Set the HTTP verb
534 */
535 method = fr_table_value_by_substr(http_method_table, method_vb->vb_strvalue, -1, REST_HTTP_METHOD_UNKNOWN);
536 if (method != REST_HTTP_METHOD_UNKNOWN) {
537 section->request.method = method;
538 /*
539 * If the method is unknown, it's a custom verb
540 */
541 } else {
543 MEM(section->request.method_str = talloc_bstrndup(rctx, method_vb->vb_strvalue, method_vb->vb_length));
544 }
545
546 /*
547 * Handle URI component escaping
548 */
549 if (fr_uri_escape_list(&uri_vb->vb_group, rest_uri_parts, NULL) < 0) {
550 RPEDEBUG("Failed escaping URI");
551 error:
552 talloc_free(section);
553 return XLAT_ACTION_FAIL;
554 }
555
556 /*
557 * Smush all the URI components together
558 */
560 uri_vb, &uri_vb->vb_group, FR_TYPE_STRING,
562 SIZE_MAX) < 0) {
563 REDEBUG("Concatenating URI");
564 goto error;
565 }
566
567 /*
568 * We get a connection from the pool here as the CURL object
569 * is needed to use curl_easy_escape() for escaping
570 */
571 randle = rctx->handle = rest_slab_reserve(t->slab);
572 if (!randle) return XLAT_ACTION_FAIL;
573
574 randle->request = request; /* Populate the request pointer for escape callbacks */
575 if (data_vb) section->request.body = REST_HTTP_BODY_CUSTOM;
576
577 RDEBUG2("Sending HTTP %s to \"%pV\"",
580 uri_vb);
581
582 if (header_vb) {
583 fr_value_box_list_foreach(&header_vb->vb_group, header) {
584 if (unlikely(rest_request_config_add_header(request, randle, header->vb_strvalue, true) < 0)) {
585 error_release:
586 rest_slab_release(randle);
587 goto error;
588 }
589 }
590 }
591
592 /*
593 * Configure various CURL options, and initialise the read/write
594 * context data.
595 *
596 * @todo We could extract the User-Name and password from the URL string.
597 */
598 ret = rest_request_config(MODULE_CTX(xctx->mctx->mi, t, xctx->env_data, NULL),
599 section, request, randle, section->request.method,
600 section->request.body,
601 uri_vb->vb_strvalue, data_vb ? data_vb->vb_strvalue : NULL);
602 if (ret < 0) goto error_release;
603
604 /*
605 * Send the CURL request, pre-parse headers, aggregate incoming
606 * HTTP body data into a single contiguous buffer.
607 *
608 * @fixme need to pass in thread to all xlat functions
609 */
610 ret = fr_curl_io_request_enqueue(t->mhandle, request, randle);
611 if (ret < 0) goto error_release;
612
614}
615
617{
619 rlm_rest_call_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
620 rlm_rest_section_t const *section = &env->section->section;
621 fr_curl_io_request_t *handle = talloc_get_type_abort(mctx->rctx, fr_curl_io_request_t);
622
623 int hcode;
625 int ret;
626
627 if (section->tls.extract_cert_attrs) fr_curl_response_certinfo(request, handle);
628
629 if (rlm_rest_status_update(request, handle) < 0) {
630 rcode = RLM_MODULE_FAIL;
631 goto finish;
632 }
633
634 hcode = rest_get_handle_code(handle);
635 switch (hcode) {
636 case 404:
637 case 410:
638 rcode = RLM_MODULE_NOTFOUND;
639 break;
640
641 case 403:
642 rcode = RLM_MODULE_DISALLOW;
643 break;
644
645 case 401:
646 /*
647 * Attempt to parse content if there was any.
648 */
649 ret = rest_response_decode(inst, section, request, handle);
650 if (ret < 0) {
651 rcode = RLM_MODULE_FAIL;
652 break;
653 }
654
655 rcode = RLM_MODULE_REJECT;
656 break;
657
658 case 204:
659 rcode = RLM_MODULE_OK;
660 break;
661
662 default:
663 /*
664 * Attempt to parse content if there was any.
665 */
666 if ((hcode >= 200) && (hcode < 300)) {
667 ret = rest_response_decode(inst, section, request, handle);
668 if (ret < 0) rcode = RLM_MODULE_FAIL;
669 else if (ret == 0) rcode = RLM_MODULE_OK;
670 else rcode = RLM_MODULE_UPDATED;
671 break;
672 } else if (hcode < 500) {
673 rcode = RLM_MODULE_INVALID;
674 } else {
675 rcode = RLM_MODULE_FAIL;
676 }
677 }
678
679 switch (rcode) {
681 case RLM_MODULE_FAIL:
683 rest_response_error(request, handle);
684 break;
685
686 default:
687 rest_response_debug(request, handle);
688 break;
689 }
690
691finish:
692 rest_slab_release(handle);
693
694 RETURN_UNLANG_RCODE(rcode);
695}
696
697/*
698 * Find the named user in this modules database. Create the set
699 * of attribute-value pairs to check and reply with for this user
700 * from the database. The authentication code only needs to check
701 * the password, the rest is done here.
702 */
703static unlang_action_t CC_HINT(nonnull) mod_common(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
704{
705 rlm_rest_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_rest_thread_t);
706 rlm_rest_call_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
707 rlm_rest_section_t const *section = &env->section->section;
708
709 void *handle;
710 int ret;
711
712 handle = rest_slab_reserve(t->slab);
713 if (!handle) RETURN_UNLANG_FAIL;
714
715 ret = rlm_rest_perform(mctx, section, handle, request);
716 if (ret < 0) {
717 rest_slab_release(handle);
718
720 }
721
723}
724
726 module_ctx_t const *mctx, request_t *request)
727{
729 rlm_rest_call_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
730 rlm_rest_section_t const *section = &env->section->section;
731 fr_curl_io_request_t *handle = talloc_get_type_abort(mctx->rctx, fr_curl_io_request_t);
732
733 int hcode;
734 int rcode = RLM_MODULE_OK;
735 int ret;
736
737 if (section->tls.extract_cert_attrs) fr_curl_response_certinfo(request, handle);
738
739 if (rlm_rest_status_update(request, handle) < 0) {
740 rcode = RLM_MODULE_FAIL;
741 goto finish;
742 }
743
744 hcode = rest_get_handle_code(handle);
745 switch (hcode) {
746 case 404:
747 case 410:
748 rcode = RLM_MODULE_NOTFOUND;
749 break;
750
751 case 403:
752 rcode = RLM_MODULE_DISALLOW;
753 break;
754
755 case 401:
756 /*
757 * Attempt to parse content if there was any.
758 */
759 ret = rest_response_decode(inst, section, request, handle);
760 if (ret < 0) {
761 rcode = RLM_MODULE_FAIL;
762 break;
763 }
764
765 rcode = RLM_MODULE_REJECT;
766 break;
767
768 case 204:
769 rcode = RLM_MODULE_OK;
770 break;
771
772 default:
773 /*
774 * Attempt to parse content if there was any.
775 */
776 if ((hcode >= 200) && (hcode < 300)) {
777 ret = rest_response_decode(inst, section, request, handle);
778 if (ret < 0) rcode = RLM_MODULE_FAIL;
779 else if (ret == 0) rcode = RLM_MODULE_OK;
780 else rcode = RLM_MODULE_UPDATED;
781 break;
782 } else if (hcode < 500) {
783 rcode = RLM_MODULE_INVALID;
784 } else {
785 rcode = RLM_MODULE_FAIL;
786 }
787 }
788
789 switch (rcode) {
791 case RLM_MODULE_FAIL:
793 rest_response_error(request, handle);
794 break;
795
796 default:
797 rest_response_debug(request, handle);
798 break;
799 }
800
801finish:
802 rest_slab_release(handle);
803
804 RETURN_UNLANG_RCODE(rcode);
805}
806
807/*
808 * Authenticate the user with the given password.
809 */
810static unlang_action_t CC_HINT(nonnull) mod_authenticate(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
811{
812 rlm_rest_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_rest_thread_t);
813 rlm_rest_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
814 rlm_rest_section_t const *section = &call_env->section->section;
815 fr_curl_io_request_t *handle;
816 int ret;
817
818 /*
819 * We can only authenticate user requests which HAVE
820 * a User-Name attribute.
821 */
822 if (!call_env->request.username) {
823 REDEBUG("Attribute \"User-Name\" is required for authentication");
825 }
826
827 if (!call_env->request.password) {
828 REDEBUG("Attribute \"User-Password\" is required for authentication");
830 }
831
832 /*
833 * Make sure the supplied password isn't empty
834 */
835 if (call_env->request.password->vb_length == 0) {
836 REDEBUG("User-Password must not be empty");
838 }
839
840 /*
841 * Log the password
842 */
843 if (RDEBUG_ENABLED3) {
844 RDEBUG("Login attempt with password \"%pV\"", call_env->request.password);
845 } else {
846 RDEBUG2("Login attempt with password");
847 }
848
849 handle = rest_slab_reserve(t->slab);
850 if (!handle) RETURN_UNLANG_FAIL;
851
852 ret = rlm_rest_perform(mctx, section, handle, request);
853 if (ret < 0) {
854 rest_slab_release(handle);
855
857 }
858
860}
861
863{
865 rlm_rest_call_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
866 rlm_rest_section_t const *section = &env->section->section;
867 fr_curl_io_request_t *handle = talloc_get_type_abort(mctx->rctx, fr_curl_io_request_t);
868
869 int hcode;
870 int rcode = RLM_MODULE_OK;
871 int ret;
872
873 if (section->tls.extract_cert_attrs) fr_curl_response_certinfo(request, handle);
874
875 if (rlm_rest_status_update(request, handle) < 0) {
876 rcode = RLM_MODULE_FAIL;
877 goto finish;
878 }
879
880 hcode = rest_get_handle_code(handle);
881 if (hcode >= 500) {
882 rcode = RLM_MODULE_FAIL;
883 } else if (hcode == 204) {
884 rcode = RLM_MODULE_OK;
885 } else if ((hcode >= 200) && (hcode < 300)) {
886 ret = rest_response_decode(inst, section, request, handle);
887 if (ret < 0) rcode = RLM_MODULE_FAIL;
888 else if (ret == 0) rcode = RLM_MODULE_OK;
889 else rcode = RLM_MODULE_UPDATED;
890 } else {
891 rcode = RLM_MODULE_INVALID;
892 }
893
894 switch (rcode) {
896 case RLM_MODULE_FAIL:
897 rest_response_error(request, handle);
898 break;
899
900 default:
901 rest_response_debug(request, handle);
902 break;
903 }
904
905finish:
906 rest_slab_release(handle);
907
908 RETURN_UNLANG_RCODE(rcode);
909}
910
911/*
912 * Send accounting info to a REST API endpoint
913 */
914static unlang_action_t CC_HINT(nonnull) mod_accounting(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
915{
916 rlm_rest_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_rest_thread_t);
917 rlm_rest_call_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
918 rlm_rest_section_t const *section = &env->section->section;
919 void *handle;
920 int ret;
921
922 handle = rest_slab_reserve(t->slab);
923 if (!handle) RETURN_UNLANG_FAIL;
924
925 ret = rlm_rest_perform(mctx, section, handle, request);
926 if (ret < 0) {
927 rest_slab_release(handle);
928
930 }
931
933}
934
936 rlm_rest_section_t *config, char const *name, CONF_SECTION *cs)
937{
938 CONF_SECTION *request_cs;
939
940 if (!cs) cs = cf_section_find(parent, name, NULL);
941 if (!cs) {
942 config->name = NULL;
943 return 0;
944 }
945
946 if (cf_section_rules_push(cs, config_items) < 0) return -1;
947 if (cf_section_parse(inst, config, cs) < 0) {
948 config->name = NULL;
949 return -1;
950 }
951
952 /*
953 * Add section name (Maybe add to headers later?).
954 */
955 config->name = name;
956
957 /*
958 * Convert HTTP method auth and body type strings into their integer equivalents.
959 */
960 if ((config->request.auth != REST_HTTP_AUTH_NONE) && !http_curl_auth[config->request.auth]) {
961 cf_log_err(cs, "Unsupported HTTP auth type \"%s\", check libcurl version, OpenSSL build "
962 "configuration, then recompile this module",
963 fr_table_str_by_value(http_auth_table, config->request.auth, "<INVALID>"));
964
965 return -1;
966 }
967 config->request.method = fr_table_value_by_str(http_method_table, config->request.method_str, REST_HTTP_METHOD_CUSTOM);
968
969 /*
970 * Custom hackery to figure out if data was set we can't do it any other way because we can't
971 * parse the tmpl_t except within a call_env.
972 *
973 * We have custom body data so we set REST_HTTP_BODY_CUSTOM, but also need to try and
974 * figure out what content-type to use. So if they've used the canonical form we
975 * need to convert it back into a proper HTTP content_type value.
976 */
977 if ((strcmp(name, "xlat") == 0) || ((request_cs = cf_section_find(cs, "request", NULL)) && cf_pair_find(request_cs, "data"))) {
978 http_body_type_t body;
979
980 config->request.body = REST_HTTP_BODY_CUSTOM;
981
983 if (body != REST_HTTP_BODY_UNKNOWN) {
984 config->request.body_str = fr_table_str_by_value(http_content_type_table, body, config->request.body_str);
985 }
986 /*
987 * We don't have any custom user data, so we need to select the right encoder based
988 * on the body type.
989 *
990 * To make this slightly more/less confusing, we accept both canonical body_types,
991 * and content_types.
992 */
993 } else {
995 if (config->request.body == REST_HTTP_BODY_UNKNOWN) {
997 }
998
999 if (config->request.body == REST_HTTP_BODY_UNKNOWN) {
1000 cf_log_err(cs, "Unknown HTTP body type '%s'", config->request.body_str);
1001 return -1;
1002 }
1003
1004 switch (http_body_type_supported[config->request.body]) {
1006 cf_log_err(cs, "Unsupported HTTP body type \"%s\", please submit patches",
1007 config->request.body_str);
1008 return -1;
1009
1011 cf_log_err(cs, "Invalid HTTP body type. \"%s\" is not a valid web API data "
1012 "markup format", config->request.body_str);
1013 return -1;
1014
1016 cf_log_err(cs, "Unavailable HTTP body type. \"%s\" is not available in this "
1017 "build", config->request.body_str);
1018 return -1;
1019
1020 default:
1021 break;
1022 }
1023 }
1024
1025 if (config->response.force_to_str) {
1026 config->response.force_to = fr_table_value_by_str(http_body_type_table, config->response.force_to_str, REST_HTTP_BODY_UNKNOWN);
1027 if (config->response.force_to == REST_HTTP_BODY_UNKNOWN) {
1028 config->response.force_to = fr_table_value_by_str(http_content_type_table, config->response.force_to_str, REST_HTTP_BODY_UNKNOWN);
1029 }
1030
1031 if (config->response.force_to == REST_HTTP_BODY_UNKNOWN) {
1032 cf_log_err(cs, "Unknown forced response body type '%s'", config->response.force_to_str);
1033 return -1;
1034 }
1035
1036 switch (http_body_type_supported[config->response.force_to]) {
1038 cf_log_err(cs, "Unsupported forced response body type \"%s\", please submit patches",
1039 config->response.force_to_str);
1040 return -1;
1041
1043 cf_log_err(cs, "Invalid HTTP forced response body type. \"%s\" is not a valid web API data "
1044 "markup format", config->response.force_to_str);
1045 return -1;
1046
1047 default:
1048 break;
1049 }
1050 }
1051
1052 return 0;
1053}
1054
1055/** Cleans up after a REST request.
1056 *
1057 * Resets all options associated with a CURL handle, and frees any headers
1058 * associated with it.
1059 *
1060 * @param[in] randle to cleanup.
1061 * @param[in] uctx unused.
1062 */
1064{
1065 rlm_rest_curl_context_t *ctx = talloc_get_type_abort(randle->uctx, rlm_rest_curl_context_t);
1066 CURL *candle = randle->candle;
1067
1068 /*
1069 * Clear any previously configured options
1070 */
1071 curl_easy_reset(candle);
1072
1073 /*
1074 * Free header list
1075 */
1076 if (ctx->headers != NULL) {
1077 curl_slist_free_all(ctx->headers);
1078 ctx->headers = NULL;
1079 }
1080
1081#ifndef NDEBUG
1082 {
1083 CURLcode ret;
1084 /*
1085 * With curl 7.61 when a request in cancelled we get a result
1086 * with a NULL (invalid) pointer to private data. This lets
1087 * us know that the request was returned to the slab.
1088 */
1089 ret = curl_easy_setopt(candle, CURLOPT_PRIVATE, (void *)0xdeadc341);
1090 if (unlikely(ret != CURLE_OK)) {
1091 ERROR("Failed to set private data on curl easy handle %p: %s",
1092 candle, curl_easy_strerror(ret));
1093 }
1094 }
1095#endif
1096
1097 /*
1098 * Free response data
1099 */
1100 TALLOC_FREE(ctx->body);
1101 TALLOC_FREE(ctx->response.buffer);
1102 TALLOC_FREE(ctx->request.encoder);
1103 TALLOC_FREE(ctx->response.decoder);
1104 ctx->response.header = NULL; /* This is owned by the parsed call env and must not be freed */
1105
1106 randle->request = NULL;
1107 return 0;
1108}
1109
1111{
1112 curl_easy_cleanup(randle->candle);
1113 return 0;
1114}
1115
1116static int rest_conn_alloc(fr_curl_io_request_t *randle, void *uctx)
1117{
1118 rlm_rest_t const *inst = talloc_get_type_abort(uctx, rlm_rest_t);
1119 rlm_rest_curl_context_t *curl_ctx = NULL;
1120
1121 randle->candle = curl_easy_init();
1122 if (unlikely(!randle->candle)) {
1123 fr_strerror_printf("Unable to initialise CURL handle");
1124 return -1;
1125 }
1126
1127 MEM(curl_ctx = talloc_zero(randle, rlm_rest_curl_context_t));
1128 curl_ctx->headers = NULL;
1129 curl_ctx->request.instance = inst;
1130 curl_ctx->response.instance = inst;
1131
1132 randle->uctx = curl_ctx;
1133 talloc_set_destructor(randle, _mod_conn_free);
1134
1135 rest_slab_element_set_destructor(randle, _rest_request_cleanup, NULL);
1136
1137 return 0;
1138}
1139
1140/** Create a thread specific multihandle
1141 *
1142 * Easy handles representing requests are added to the curl multihandle
1143 * with the multihandle used for mux/demux.
1144 *
1145 * @param[in] mctx Thread instantiation data.
1146 * @return
1147 * - 0 on success.
1148 * - -1 on failure.
1149 */
1151{
1152 rlm_rest_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_rest_t);
1153 rlm_rest_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_rest_thread_t);
1154 fr_curl_handle_t *mhandle;
1155
1156 t->inst = inst;
1157
1158 if (!(t->slab = rest_slab_list_alloc(t, mctx->el, &inst->conn_config.reuse,
1159 rest_conn_alloc, NULL, inst, false, false))) {
1160 ERROR("Connection handle pool instantiation failed");
1161 return -1;
1162 }
1163
1164 mhandle = fr_curl_io_init(t, mctx->el, inst->multiplex);
1165 if (!mhandle) return -1;
1166
1167 t->mhandle = mhandle;
1168
1169 return 0;
1170}
1171
1172/** Cleanup all outstanding requests associated with this thread
1173 *
1174 * Destroys all curl easy handles, and then the multihandle associated
1175 * with this thread.
1176 *
1177 * @param[in] mctx data to destroy.
1178 * @return 0
1179 */
1181{
1182 rlm_rest_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_rest_thread_t);
1183
1184 talloc_free(t->mhandle); /* Ensure this is shutdown before the pool */
1185 talloc_free(t->slab);
1186
1187 return 0;
1188}
1189
1190/*
1191 * Do any per-module initialization that is separate to each
1192 * configured instance of the module. e.g. set up connections
1193 * to external databases, read configuration files, set up
1194 * dictionary entries, etc.
1195 *
1196 * If configuration information is given in the config section
1197 * that must be referenced in later calls, store a handle to it
1198 * in *instance otherwise put a null pointer there.
1199 */
1200static int mod_instantiate(module_inst_ctx_t const *mctx)
1201{
1202 rlm_rest_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_rest_t);
1203 CONF_SECTION *conf = mctx->mi->conf;
1204 rlm_rest_section_conf_t *section;
1206
1207 inst->xlat.request.method_str = "GET";
1208 inst->xlat.request.body = REST_HTTP_BODY_NONE;
1209 inst->xlat.request.body_str = "application/x-www-form-urlencoded";
1210 inst->xlat.response.force_to_str = "plain";
1211
1212 if (!inst->sections_init) fr_rb_inline_init(&inst->sections, rlm_rest_section_conf_t, node, rest_section_cmp, NULL);
1213
1214 /*
1215 * Parse xlat config.
1216 */
1217 if ((parse_sub_section(inst, conf, xlat_config, &inst->xlat, "xlat", NULL) < 0)) return -1;
1218
1219 /*
1220 * Parse section configs from calls found by the call_env parser.
1221 */
1222 section = fr_rb_iter_init_inorder(&iter, &inst->sections);
1223 while (section) {
1225 cf_section_name(section->cs), section->cs) < 0) return -1;
1226 section = fr_rb_iter_next_inorder(&iter);
1227 }
1228
1229 inst->conn_config.reuse.num_children = 1;
1230 inst->conn_config.reuse.child_pool_size = sizeof(rlm_rest_curl_context_t);
1231
1232 return 0;
1233}
1234
1235static int mod_bootstrap(module_inst_ctx_t const *mctx)
1236{
1237 xlat_t *xlat;
1238
1239 xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, NULL, rest_xlat, FR_TYPE_STRING);
1242
1243 return 0;
1244}
1245
1246static int mod_load(void)
1247{
1248 /* developer sanity */
1250
1251#ifdef HAVE_JSON
1253#endif
1254
1255 return 0;
1256}
1257
1258/*
1259 * Custom call_env parser which looks for a conf section matching the name
1260 * of the section the module is called in and then hands off to the normal
1261 * parsing.
1262 */
1263static int rest_sect_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, UNUSED tmpl_rules_t const *t_rules,
1264 CONF_ITEM *ci, call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
1265{
1266 rlm_rest_t *inst = talloc_get_type_abort(cec->mi->data, rlm_rest_t);
1267 CONF_SECTION *cs;
1268 CONF_SECTION *sect = NULL;
1269 call_env_parsed_t *parsed;
1270 void *found;
1271 rlm_rest_section_conf_t *section;
1272 char *p, *name2 = NULL;
1273 size_t i;
1274
1275 /*
1276 * The parent section is the main module conf section
1277 * in which we'll look for a suitable section to parse.
1278 */
1279 cs = cf_item_to_section(cf_parent(ci));
1280
1281 if (cec->asked->name2) {
1282 name2 = talloc_strdup(NULL, cec->asked->name2);
1283 p = name2;
1284 for (i = 0; i < talloc_array_length(name2); i++) {
1285 *p = tolower(*p);
1286 p++;
1287 }
1288 sect = cf_section_find(cs, cec->asked->name1, name2);
1289 }
1290
1291 if (!sect) {
1292 sect = cf_section_find(cs, cec->asked->name1, NULL);
1293 }
1294
1295 if (!inst->sections_init) {
1297 inst->sections_init = true;
1298 }
1299
1300 if (!sect) {
1301 cf_log_err(cs, "%s called in %s %s - requires conf section %s %s%s%s", cec->mi->name,
1302 cec->asked->name1, cec->asked->name2 ? cec->asked->name2 : "",
1303 cec->asked->name1, cec->asked->name2 ? name2 : "",
1304 cec->asked->name2 ? " or " : "",
1305 cec->asked->name2 ? cec->asked->name1 : "");
1306 talloc_free(name2);
1307 return -1;
1308 }
1309 talloc_free(name2);
1310
1311 /*
1312 * "authenticate" sections use a different rules with defaults set for username and password
1313 */
1314 if (strcmp(cec->asked->name1, "authenticate") == 0) {
1315 call_env_parse(ctx, out, cec->mi->name, t_rules, sect, cec, rest_section_authenticate_env);
1316 } else {
1317 call_env_parse(ctx, out, cec->mi->name, t_rules, sect, cec, rest_section_common_env);
1318 }
1319 parsed = call_env_parsed_add(ctx, out,
1321 .name = "section",
1322 .flags = CALL_ENV_FLAG_PARSE_ONLY,
1323 .pair = {
1324 .parsed = {
1325 .offset = offsetof(rlm_rest_call_env_t, section),
1327 }
1328 }
1329 });
1330
1331 MEM(section = talloc_zero(inst, rlm_rest_section_conf_t));
1332 section->cs = sect;
1333 if (fr_rb_find_or_insert(&found, &inst->sections, section) < 0) {
1334 talloc_free(section);
1335 return -1;
1336 }
1337 if (found) {
1338 talloc_free(section);
1339 call_env_parsed_set_data(parsed, found);
1340 } else {
1341 call_env_parsed_set_data(parsed, section);
1342 }
1343 return 0;
1344};
1345
1353
1354/*
1355 * The module name should be the only globally exported symbol.
1356 * That is, everything else should be 'static'.
1357 *
1358 * If the module needs to temporarily modify it's instantiation
1359 * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
1360 * The server will then take care of ensuring that the module
1361 * is single-threaded.
1362 */
1363extern module_rlm_t rlm_rest;
1365 .common = {
1366 .magic = MODULE_MAGIC_INIT,
1367 .name = "rest",
1368 .inst_size = sizeof(rlm_rest_t),
1369 .thread_inst_size = sizeof(rlm_rest_thread_t),
1370 .config = module_config,
1371 .onload = mod_load,
1372 .bootstrap = mod_bootstrap,
1373 .instantiate = mod_instantiate,
1374 .thread_instantiate = mod_thread_instantiate,
1375 .thread_detach = mod_thread_detach
1376 },
1377 .method_group = {
1378 .bindings = (module_method_binding_t[]){
1379 { .section = SECTION_NAME("recv", "accounting-request"), .method = mod_accounting, .method_env = &rest_method_env },
1380 { .section = SECTION_NAME("accounting", CF_IDENT_ANY), .method = mod_accounting, .method_env = &rest_method_env },
1381 { .section = SECTION_NAME("authenticate", CF_IDENT_ANY), .method = mod_authenticate, .method_env = &rest_method_env },
1382 { .section = SECTION_NAME("send", CF_IDENT_ANY), .method = mod_accounting, .method_env = &rest_method_env },
1383 { .section = SECTION_NAME(CF_IDENT_ANY, CF_IDENT_ANY), .method = mod_common, .method_env = &rest_method_env },
1385 }
1386 }
1387};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
#define fr_atexit_thread_local(_name, _free, _uctx)
Definition atexit.h:221
#define RCSID(id)
Definition build.h:485
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition build.h:112
#define unlikely(_x)
Definition build.h:383
#define UNUSED
Definition build.h:317
#define NUM_ELEMENTS(_t)
Definition build.h:339
int call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *parsed, char const *name, tmpl_rules_t const *t_rules, CONF_SECTION const *cs, call_env_ctx_t const *cec, call_env_parser_t const *rule)
Parse per call env.
Definition call_env.c:466
call_env_parsed_t * call_env_parsed_add(TALLOC_CTX *ctx, call_env_parsed_head_t *head, call_env_parser_t const *rule)
Allocate a new call_env_parsed_t structure and add it to the list of parsed call envs.
Definition call_env.c:688
void call_env_parsed_set_data(call_env_parsed_t *parsed, void const *data)
Assign data to a call_env_parsed_t.
Definition call_env.c:745
#define CALL_ENV_TERMINATOR
Definition call_env.h:236
#define FR_CALL_ENV_METHOD_OUT(_inst)
Helper macro for populating the size/type fields of a call_env_method_t from the output structure typ...
Definition call_env.h:240
call_env_parser_t const * env
Parsing rules for call method env.
Definition call_env.h:247
section_name_t const * asked
The actual name1/name2 that resolved to a module_method_binding_t.
Definition call_env.h:232
#define FR_CALL_ENV_SUBSECTION(_name, _name2, _flags, _subcs)
Specify a call_env_parser_t which defines a nested subsection.
Definition call_env.h:402
@ CALL_ENV_FLAG_PARSE_ONLY
The result of parsing will not be evaluated at runtime.
Definition call_env.h:85
@ CALL_ENV_FLAG_NONE
Definition call_env.h:74
@ CALL_ENV_FLAG_PARSE_MISSING
If this subsection is missing, still parse it.
Definition call_env.h:88
@ CALL_ENV_PARSE_TYPE_VOID
Output of the parsing phase is undefined (a custom structure).
Definition call_env.h:62
module_instance_t const * mi
Module instance that the callenv is registered to.
Definition call_env.h:229
#define FR_CALL_ENV_SUBSECTION_FUNC(_name, _name2, _flags, _func)
Specify a call_env_parser_t which parses a subsection using a callback function.
Definition call_env.h:412
Per method call config.
Definition call_env.h:180
int cf_section_parse(TALLOC_CTX *ctx, void *base, CONF_SECTION *cs)
Parse a configuration section into user-supplied variables.
Definition cf_parse.c:1195
int cf_table_parse_int(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
Generic function for parsing conf pair values as int.
Definition cf_parse.c:1633
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:662
cf_parse_t func
Override default parsing behaviour for the specified type with a custom parsing function.
Definition cf_parse.h:616
#define FR_CONF_DEPRECATED(_name, _struct, _field)
conf_parser_t entry which raises an error if a matching CONF_PAIR is found
Definition cf_parse.h:414
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:284
#define cf_section_rules_push(_cs, _rule)
Definition cf_parse.h:694
#define FR_CONF_OFFSET_SUBSECTION(_name, _flags, _struct, _field, _subcs)
conf_parser_t which populates a sub-struct using a CONF_SECTION
Definition cf_parse.h:313
#define FR_CONF_OFFSET_TYPE_FLAGS(_name, _type, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:241
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:599
Common header for all CONF_* types.
Definition cf_priv.h:49
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
Definition cf_util.c:1027
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:683
CONF_PAIR * cf_pair_find(CONF_SECTION const *cs, char const *attr)
Search for a CONF_PAIR with a specific name.
Definition cf_util.c:1426
char const * cf_section_name(CONF_SECTION const *cs)
Return name2 if set, else name1.
Definition cf_util.c:1196
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition cf_util.c:663
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1581
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:286
#define cf_parent(_cf)
Definition cf_util.h:101
#define CF_IDENT_ANY
Definition cf_util.h:78
fr_curl_handle_t * fr_curl_io_init(TALLOC_CTX *ctx, fr_event_list_t *el, bool multiplex)
bool extract_cert_attrs
Definition base.h:119
request_t * request
Current request.
Definition base.h:104
void * uctx
Private data for the module using the API.
Definition base.h:105
int fr_curl_io_request_enqueue(fr_curl_handle_t *mhandle, request_t *request, fr_curl_io_request_t *creq)
Sends a request using libcurl.
Definition io.c:480
CURL * candle
Request specific handle.
Definition base.h:102
Uctx data for timer and I/O functions.
Definition base.h:91
Structure representing an individual request being passed to curl for processing.
Definition base.h:101
#define CURL_URI_SAFE_FOR
safe for value suitable for all users of the curl library
Definition xlat.h:36
static int fr_dcursor_insert(fr_dcursor_t *cursor, void *v)
Insert directly after the current item.
Definition dcursor.h:437
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:41
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:275
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:288
static fr_slen_t in
Definition dict.h:849
Specifies an attribute which must be present for the module to function.
Definition dict.h:274
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:287
Test enumeration values.
Definition dict_test.h:92
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
#define GLOBAL_LIB_TERMINATOR
Definition global_lib.h:51
Structure to define how to initialise libraries with global configuration.
Definition global_lib.h:38
static xlat_action_t rest_xlat(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Simple xlat to read text data from a URL.
Definition rlm_rest.c:502
void fr_json_version_print(void)
Print JSON-C version.
Definition json.c:458
int fr_curl_response_certinfo(request_t *request, fr_curl_io_request_t *randle)
Definition base.c:170
global_lib_autoinst_t fr_curl_autoinst
Definition base.c:387
CURL * fr_curl_tmp_handle(void)
Return a thread local curl easy handle.
Definition base.c:267
conf_parser_t fr_curl_conn_config[]
Definition base.c:97
conf_parser_t fr_curl_tls_config[]
Definition base.c:68
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:443
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition log.h:335
#define RPEDEBUG(fmt,...)
Definition log.h:376
#define RINDENT()
Indent R* messages by one level.
Definition log.h:430
talloc_free(reap)
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_SIZE
Unsigned integer capable of representing any memory address on the local system.
long int ssize_t
void * env_data
Per call environment data.
Definition module_ctx.h:44
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:42
void * thread
Thread specific instance data.
Definition module_ctx.h:43
void * rctx
Resume ctx that a module previously set.
Definition module_ctx.h:45
fr_event_list_t * el
Event list to register any IO handlers and timers against.
Definition module_ctx.h:68
void * thread
Thread instance data.
Definition module_ctx.h:67
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:64
#define MODULE_CTX(_mi, _thread, _env_data, _rctx)
Wrapper to create a module_ctx_t as a compound literal.
Definition module_ctx.h:128
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for module calls.
Definition module_ctx.h:41
Temporary structure to hold arguments for instantiation calls.
Definition module_ctx.h:50
Temporary structure to hold arguments for thread_instantiation calls.
Definition module_ctx.h:63
xlat_t * module_rlm_xlat_register(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx, char const *name, xlat_func_t func, fr_type_t return_type)
Definition module_rlm.c:243
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
void rest_io_module_signal(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
Handle asynchronous cancellation of a request.
Definition io.c:33
void rest_io_xlat_signal(xlat_ctx_t const *xctx, request_t *request, fr_signal_t action)
Handle asynchronous cancellation of a request.
Definition io.c:56
int fr_pair_value_bstrndup(fr_pair_t *vp, char const *src, size_t len, bool tainted)
Copy data into a "string" type value pair.
Definition pair.c:2788
static const conf_parser_t config[]
Definition base.c:186
#define fr_assert(_expr)
Definition rad_assert.h:38
#define pair_update_request(_attr, _da)
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define RDEBUG(fmt,...)
Definition radclient.h:53
static rs_t * conf
Definition radsniff.c:53
void * fr_rb_iter_init_inorder(fr_rb_iter_inorder_t *iter, fr_rb_tree_t *tree)
Initialise an in-order iterator.
Definition rb.c:824
int fr_rb_find_or_insert(void **found, fr_rb_tree_t *tree, void const *data)
Attempt to find current data in the tree, if it does not exist insert it.
Definition rb.c:598
void * fr_rb_iter_next_inorder(fr_rb_iter_inorder_t *iter)
Return the next node.
Definition rb.c:850
#define fr_rb_inline_init(_tree, _type, _field, _data_cmp, _data_free)
Initialises a red black tree.
Definition rb.h:180
Iterator structure for in-order traversal of an rbtree.
Definition rb.h:321
#define RETURN_UNLANG_INVALID
Definition rcode.h:62
#define RETURN_UNLANG_RCODE(_rcode)
Definition rcode.h:57
#define RETURN_UNLANG_FAIL
Definition rcode.h:59
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:40
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition rcode.h:47
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:45
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:44
@ RLM_MODULE_DISALLOW
Reject the request (user is locked out).
Definition rcode.h:48
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition rcode.h:43
@ RLM_MODULE_NOTFOUND
User not found.
Definition rcode.h:49
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition rcode.h:51
fr_table_num_sorted_t const http_auth_table[]
Definition rest.c:163
int rest_request_config(module_ctx_t const *mctx, rlm_rest_section_t const *section, request_t *request, fr_curl_io_request_t *randle, http_method_t method, http_body_type_t type, char const *uri, char const *body_data)
Configures request curlopts.
Definition rest.c:1787
fr_table_num_sorted_t const http_body_type_table[]
Conversion table for type config values.
Definition rest.c:147
fr_table_num_sorted_t const http_method_table[]
Conversion table for method config values.
Definition rest.c:128
size_t rest_get_handle_data(char const **out, fr_curl_io_request_t *randle)
Extracts pointer to buffer containing response data.
Definition rest.c:1623
void rest_response_debug(request_t *request, fr_curl_io_request_t *handle)
Print out the response text.
Definition rest.c:1566
fr_table_num_sorted_t const http_content_type_table[]
Conversion table for "Content-Type" header values.
Definition rest.c:191
const unsigned long http_curl_auth[REST_HTTP_AUTH_NUM_ENTRIES]
Definition rest.c:102
const http_body_type_t http_body_type_supported[REST_HTTP_BODY_NUM_ENTRIES]
Table of encoder/decoder support.
Definition rest.c:52
int rest_request_config_add_header(request_t *request, fr_curl_io_request_t *randle, char const *header, bool validate)
Adds an additional header to a handle to use in the next reques.
Definition rest.c:1707
void rest_response_error(request_t *request, fr_curl_io_request_t *handle)
Print out the response text as error lines.
Definition rest.c:1541
int rest_response_decode(rlm_rest_t const *instance, rlm_rest_section_t const *section, request_t *request, fr_curl_io_request_t *randle)
Sends the response to the correct decode function.
Definition rest.c:2087
Function prototypes and datatypes for the REST (HTTP) transport.
rlm_rest_t const * instance
This instance of rlm_rest.
Definition rest.h:238
struct curl_slist * headers
Any HTTP headers which will be sent with the request.
Definition rest.h:262
tmpl_t * header
Where to create pairs representing HTTP response headers.
Definition rest.h:252
#define rest_get_handle_code(_handle)
Definition rest.h:330
char * buffer
Raw incoming HTTP data.
Definition rest.h:244
fr_curl_handle_t * mhandle
Thread specific multi handle.
Definition rest.h:192
char * body
Pointer to the buffer which contains body data/ Only used when not performing chunked encoding.
Definition rest.h:265
fr_curl_tls_t tls
Definition rest.h:146
http_method_t
Definition rest.h:43
@ REST_HTTP_METHOD_UNKNOWN
Definition rest.h:44
@ REST_HTTP_METHOD_CUSTOM
Must always come last, should not be in method table.
Definition rest.h:50
http_body_type_t
Definition rest.h:53
@ REST_HTTP_BODY_INVALID
Definition rest.h:57
@ REST_HTTP_BODY_UNSUPPORTED
Definition rest.h:55
@ REST_HTTP_BODY_CUSTOM
Definition rest.h:59
@ REST_HTTP_BODY_NUM_ENTRIES
Definition rest.h:67
@ REST_HTTP_BODY_UNKNOWN
Definition rest.h:54
@ REST_HTTP_BODY_NONE
Definition rest.h:58
@ REST_HTTP_BODY_UNAVAILABLE
Definition rest.h:56
char const * method_str
The string version of the HTTP method.
Definition rest.h:112
struct rlm_rest_call_env_t::@186 request
rlm_rest_section_t section
Parsed section config.
Definition rest.h:153
rlm_rest_section_conf_t * section
Section config.
Definition rest.h:281
rlm_rest_section_request_t request
Request configuration.
Definition rest.h:143
rlm_rest_response_t response
Response context data.
Definition rest.h:269
void * decoder
Decoder specific data.
Definition rest.h:255
void * encoder
Encoder specific data.
Definition rest.h:230
rlm_rest_request_t request
Request context data.
Definition rest.h:268
http_method_t method
What HTTP method should be used, GET, POST etc...
Definition rest.h:113
rlm_rest_section_t section
Our mutated section config.
Definition rest.h:276
rlm_rest_t const * inst
Instance of rlm_rest.
Definition rest.h:190
@ REST_HTTP_AUTH_NONE
Definition rest.h:72
rest_slab_list_t * slab
Slab list for connection handles.
Definition rest.h:191
http_body_type_t body
What encoding type should be used.
Definition rest.h:116
fr_curl_io_request_t * handle
curl easy handle servicing our request.
Definition rest.h:277
rlm_rest_t const * instance
This instance of rlm_rest.
Definition rest.h:220
CONF_SECTION * cs
Conf section found for this call.
Definition rest.h:154
Thread specific rlm_rest instance data.
Definition rest.h:189
Stores the state of a yielded xlat.
Definition rest.h:275
static char const * name
static const call_env_method_t rest_call_env_xlat
Definition rlm_rest.c:225
static int rlm_rest_status_update(request_t *request, void *handle)
Update the status attribute.
Definition rlm_rest.c:287
static int mod_load(void)
Definition rlm_rest.c:1246
static int8_t rest_section_cmp(void const *one, void const *two)
Definition rlm_rest.c:273
static int rest_conn_alloc(fr_curl_io_request_t *randle, void *uctx)
Definition rlm_rest.c:1116
static int _rest_uri_part_escape_uctx_free(void *uctx)
Definition rlm_rest.c:312
static const conf_parser_t xlat_config[]
Definition rlm_rest.c:141
static xlat_action_t rest_xlat_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, UNUSED fr_value_box_list_t *in)
Definition rlm_rest.c:400
static int parse_sub_section(rlm_rest_t *inst, CONF_SECTION *parent, conf_parser_t const *config_items, rlm_rest_section_t *config, char const *name, CONF_SECTION *cs)
Definition rlm_rest.c:935
static unlang_action_t mod_common(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_rest.c:703
fr_dict_t const * dict_freeradius
Definition rlm_rest.c:247
static int rlm_rest_perform(module_ctx_t const *mctx, rlm_rest_section_t const *section, fr_curl_io_request_t *randle, request_t *request)
Definition rlm_rest.c:370
static const conf_parser_t xlat_request_config[]
Definition rlm_rest.c:136
static fr_table_num_sorted_t const http_negotiation_table[]
Definition rlm_rest.c:53
static fr_uri_part_t const rest_uri_parts[]
Definition rlm_rest.c:44
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition rlm_rest.c:1235
fr_dict_attr_t const * attr_rest_http_header
Definition rlm_rest.c:256
static unlang_action_t mod_accounting(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_rest.c:914
fr_dict_autoload_t rlm_rest_dict[]
Definition rlm_rest.c:250
static const conf_parser_t section_request_config[]
Definition rlm_rest.c:111
static const conf_parser_t section_config[]
Definition rlm_rest.c:124
static int _rest_request_cleanup(fr_curl_io_request_t *randle, UNUSED void *uctx)
Cleans up after a REST request.
Definition rlm_rest.c:1063
#define SECTION_REQUEST_COMMON
Definition rlm_rest.c:103
static int rest_uri_part_escape(fr_value_box_t *vb, void *uctx)
static int rest_proxy_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, UNUSED conf_parser_t const *rule)
Definition rlm_rest.c:84
static const call_env_method_t rest_method_env
Definition rlm_rest.c:1346
fr_dict_attr_t const * attr_rest_http_status_code
Definition rlm_rest.c:257
static unlang_action_t mod_accounting_result(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_rest.c:862
static size_t http_negotiation_table_len
Definition rlm_rest.c:77
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
Create a thread specific multihandle.
Definition rlm_rest.c:1150
static unlang_action_t mod_authenticate(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_rest.c:810
module_rlm_t rlm_rest
Definition rlm_rest.c:1364
fr_dict_attr_t const * attr_rest_http_body
Definition rlm_rest.c:255
fr_dict_attr_autoload_t rlm_rest_dict_attr[]
Definition rlm_rest.c:260
static const conf_parser_t section_response_config[]
Definition rlm_rest.c:118
#define REST_CALL_ENV_SECTION(_var, _dflt_username, _dflt_password)
Definition rlm_rest.c:186
static const conf_parser_t module_config[]
Definition rlm_rest.c:153
static int _mod_conn_free(fr_curl_io_request_t *randle)
Definition rlm_rest.c:1110
static xlat_arg_parser_t const rest_xlat_args[]
Definition rlm_rest.c:485
static unlang_action_t mod_authenticate_result(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_rest.c:725
static int mod_thread_detach(module_thread_inst_ctx_t const *mctx)
Cleanup all outstanding requests associated with this thread.
Definition rlm_rest.c:1180
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition rlm_rest.c:1200
static unlang_action_t mod_common_result(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_rest.c:616
static void * rest_uri_part_escape_uctx_alloc(UNUSED request_t *request, void const *uctx)
Allocate an escape uctx to pass to fr_uri_escape.
Definition rlm_rest.c:323
global_lib_autoinst_t const *const rlm_rest_lib[]
Definition rlm_rest.c:268
char const * rest_no_proxy
Unique pointer used to determine if we should explicitly disable proxying.
Definition rlm_rest.c:82
static int rest_sect_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, UNUSED tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
Definition rlm_rest.c:1263
#define FR_SBUFF_TERMS(...)
Initialise a terminal structure with a list of sorted strings.
Definition sbuff.h:192
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:40
char const * name2
Second section name. Usually a packet type like 'access-request', 'access-accept',...
Definition section.h:46
char const * name1
First section name. Usually a verb like 'recv', 'send', etc...
Definition section.h:45
char const * name
Instance name e.g. user_database.
Definition module.h:355
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:349
size_t inst_size
Size of the module's instance data.
Definition module.h:212
void * data
Module's instance data.
Definition module.h:291
void * boot
Data allocated during the boostrap phase.
Definition module.h:294
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:152
Named methods exported by a module.
Definition module.h:174
#define pair_delete_request(_pair_or_da)
Delete a fr_pair_t in the request list.
Definition pair.h:172
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:332
@ FR_SIGNAL_CANCEL
Request has been cancelled.
Definition signal.h:40
unlang_action_t unlang_module_yield(request_t *request, module_method_t resume, unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition module.c:429
eap_aka_sim_process_conf_t * inst
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition table.h:653
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
#define fr_table_value_by_substr(_table, _name, _name_len, _def)
Convert a partial string to a value using an ordered or sorted table.
Definition table.h:693
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:586
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition talloc.c:467
#define talloc_get_type_abort_const
Definition talloc.h:287
xlat_action_t unlang_xlat_yield(request_t *request, xlat_func_t resume, xlat_func_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition xlat.c:548
uint8_t required
Argument must be present, and non-empty.
Definition xlat.h:146
#define XLAT_ARGS(_list,...)
Populate local variables with value boxes from the input list.
Definition xlat.h:383
#define XLAT_ARG_PARSER_TERMINATOR
Definition xlat.h:170
xlat_action_t
Definition xlat.h:37
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition xlat.h:44
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition xlat.h:43
Definition for a single argument consumend by an xlat function.
Definition xlat.h:145
int fr_uri_escape_list(fr_value_box_list_t *uri, fr_uri_part_t const *uri_parts, void *uctx)
Parse a list of value boxes representing a URI.
Definition uri.c:140
#define XLAT_URI_PART_TERMINATOR
Definition uri.h:66
char const * name
Name of this part of the URI.
Definition uri.h:47
uctx to pass to fr_uri_escape
Definition uri.h:60
Definition for a single part of a URI.
Definition uri.h:46
static fr_slen_t parent
Definition pair.h:841
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
void fr_value_box_strdup_shallow_replace(fr_value_box_t *vb, char const *src, ssize_t len)
Free the existing buffer (if talloced) associated with the valuebox, and replace it with a new one.
Definition value.c:4507
int fr_value_box_bstrndup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, size_t len, bool tainted)
Copy a string to to a fr_value_box_t.
Definition value.c:4603
int fr_value_box_list_concat_in_place(TALLOC_CTX *ctx, fr_value_box_t *out, fr_value_box_list_t *list, fr_type_t type, fr_value_box_list_action_t proc_action, bool flatten, size_t max_size)
Concatenate a list of value boxes.
Definition value.c:6281
@ FR_VALUE_BOX_LIST_FREE
Definition value.h:239
int nonnull(2, 5))
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition value.h:654
#define fr_value_box_list_foreach(_list_head, _iter)
Definition value.h:224
static size_t char ** out
Definition value.h:1023
void * rctx
Resume context.
Definition xlat_ctx.h:54
void * env_data
Expanded call env data.
Definition xlat_ctx.h:53
module_ctx_t const * mctx
Synthesised module calling ctx.
Definition xlat_ctx.h:52
An xlat calling ctx.
Definition xlat_ctx.h:49
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition xlat_func.c:363
void xlat_func_call_env_set(xlat_t *x, call_env_method_t const *env_method)
Register call environment of an xlat.
Definition xlat_func.c:389