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: cddd8da5743b9cfb06439bb242a3e14073fdce0f $
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: cddd8da5743b9cfb06439bb242a3e14073fdce0f $")
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 xa = XLAT_ACTION_FAIL;
428error:
429 rest_response_error(request, handle);
430 goto finish;
431 }
432 case 204:
433 goto finish;
434
435 default:
436 /*
437 * Attempt to parse content if there was any.
438 */
439 if ((hcode >= 200) && (hcode < 300)) {
440 break;
441 } else if (hcode < 500) {
442 xa = XLAT_ACTION_FAIL;
443 goto error;
444 } else {
445 xa = XLAT_ACTION_FAIL;
446 goto error;
447 }
448 }
449
450finish:
451 /*
452 * Always output the xlat data.
453 *
454 * The user can check REST-HTTP-Status-Code to figure out what happened.
455 *
456 * Eventually we should just emit two boxes, one with the response code
457 * and one with the body.
458 */
459 len = rest_get_handle_data(&body, handle);
460 if (len > 0) {
461 fr_value_box_t *vb;
462
463 MEM(vb = fr_value_box_alloc_null(ctx));
464 fr_value_box_bstrndup(vb, vb, NULL, body, len, true);
466 }
467
468 rest_slab_release(handle);
469
470 talloc_free(rctx);
471
472 return xa;
473}
474
476 { .required = true, .single = true, .type = FR_TYPE_STRING }, /* HTTP Method */
477 { .required = true, .safe_for = CURL_URI_SAFE_FOR, .type = FR_TYPE_STRING, .will_escape = true }, /* URL */
478 { .concat = true, .type = FR_TYPE_STRING }, /* Data */
479 { .type = FR_TYPE_STRING }, /* Headers */
481};
482
483/** Simple xlat to read text data from a URL
484 *
485 * Example:
486@verbatim
487%rest(POST, http://example.com/, "{ \"key\": \"value\" }", [<headers>])
488@endverbatim
489 *
490 * @ingroup xlat_functions
491 */
493 xlat_ctx_t const *xctx, request_t *request,
494 fr_value_box_list_t *in)
495{
497 rlm_rest_thread_t *t = talloc_get_type_abort(xctx->mctx->thread, rlm_rest_thread_t);
498
499 fr_curl_io_request_t *randle = NULL;
500 int ret;
501 http_method_t method;
502
503 fr_value_box_t *method_vb;
504 fr_value_box_t *uri_vb;
505 fr_value_box_t *data_vb;
506 fr_value_box_t *header_vb;
507
508 /* There are no configurable parameters other than the URI */
510 rlm_rest_section_t *section;
511
512 XLAT_ARGS(in, &method_vb, &uri_vb, &data_vb, &header_vb);
513
514 MEM(rctx = talloc(request, rlm_rest_xlat_rctx_t));
515 section = &rctx->section;
516
517 /*
518 * Section gets modified, so we need our own copy.
519 */
520 memcpy(&rctx->section, &inst->xlat, sizeof(*section));
521
522 /*
523 * Set the HTTP verb
524 */
525 method = fr_table_value_by_substr(http_method_table, method_vb->vb_strvalue, -1, REST_HTTP_METHOD_UNKNOWN);
526 if (method != REST_HTTP_METHOD_UNKNOWN) {
527 section->request.method = method;
528 /*
529 * If the method is unknown, it's a custom verb
530 */
531 } else {
533 MEM(section->request.method_str = talloc_bstrndup(rctx, method_vb->vb_strvalue, method_vb->vb_length));
534 }
535
536 /*
537 * Handle URI component escaping
538 */
539 if (fr_uri_escape_list(&uri_vb->vb_group, rest_uri_parts, NULL) < 0) {
540 RPEDEBUG("Failed escaping URI");
541 error:
542 talloc_free(section);
543 return XLAT_ACTION_FAIL;
544 }
545
546 /*
547 * Smush all the URI components together
548 */
550 uri_vb, &uri_vb->vb_group, FR_TYPE_STRING,
552 SIZE_MAX) < 0) {
553 REDEBUG("Concatenating URI");
554 goto error;
555 }
556
557 /*
558 * We get a connection from the pool here as the CURL object
559 * is needed to use curl_easy_escape() for escaping
560 */
561 randle = rctx->handle = rest_slab_reserve(t->slab);
562 if (!randle) return XLAT_ACTION_FAIL;
563
564 randle->request = request; /* Populate the request pointer for escape callbacks */
565 if (data_vb) section->request.body = REST_HTTP_BODY_CUSTOM;
566
567 RDEBUG2("Sending HTTP %s to \"%pV\"",
570 uri_vb);
571
572 if (header_vb) {
573 fr_value_box_list_foreach(&header_vb->vb_group, header) {
574 if (unlikely(rest_request_config_add_header(request, randle, header->vb_strvalue, true) < 0)) {
575 error_release:
576 rest_slab_release(randle);
577 goto error;
578 }
579 }
580 }
581
582 /*
583 * Configure various CURL options, and initialise the read/write
584 * context data.
585 *
586 * @todo We could extract the User-Name and password from the URL string.
587 */
588 ret = rest_request_config(MODULE_CTX(xctx->mctx->mi, t, xctx->env_data, NULL),
589 section, request, randle, section->request.method,
590 section->request.body,
591 uri_vb->vb_strvalue, data_vb ? data_vb->vb_strvalue : NULL);
592 if (ret < 0) goto error_release;
593
594 /*
595 * Send the CURL request, pre-parse headers, aggregate incoming
596 * HTTP body data into a single contiguous buffer.
597 *
598 * @fixme need to pass in thread to all xlat functions
599 */
600 ret = fr_curl_io_request_enqueue(t->mhandle, request, randle);
601 if (ret < 0) goto error_release;
602
604}
605
607{
609 rlm_rest_call_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
610 rlm_rest_section_t const *section = &env->section->section;
611 fr_curl_io_request_t *handle = talloc_get_type_abort(mctx->rctx, fr_curl_io_request_t);
612
613 int hcode;
615 int ret;
616
617 if (section->tls.extract_cert_attrs) fr_curl_response_certinfo(request, handle);
618
619 if (rlm_rest_status_update(request, handle) < 0) {
620 rcode = RLM_MODULE_FAIL;
621 goto finish;
622 }
623
624 hcode = rest_get_handle_code(handle);
625 switch (hcode) {
626 case 404:
627 case 410:
628 rcode = RLM_MODULE_NOTFOUND;
629 break;
630
631 case 403:
632 rcode = RLM_MODULE_DISALLOW;
633 break;
634
635 case 401:
636 /*
637 * Attempt to parse content if there was any.
638 */
639 ret = rest_response_decode(inst, section, request, handle);
640 if (ret < 0) {
641 rcode = RLM_MODULE_FAIL;
642 break;
643 }
644
645 rcode = RLM_MODULE_REJECT;
646 break;
647
648 case 204:
649 rcode = RLM_MODULE_OK;
650 break;
651
652 default:
653 /*
654 * Attempt to parse content if there was any.
655 */
656 if ((hcode >= 200) && (hcode < 300)) {
657 ret = rest_response_decode(inst, section, request, handle);
658 if (ret < 0) rcode = RLM_MODULE_FAIL;
659 else if (ret == 0) rcode = RLM_MODULE_OK;
660 else rcode = RLM_MODULE_UPDATED;
661 break;
662 } else if (hcode < 500) {
663 rcode = RLM_MODULE_INVALID;
664 } else {
665 rcode = RLM_MODULE_FAIL;
666 }
667 }
668
669 switch (rcode) {
671 case RLM_MODULE_FAIL:
673 rest_response_error(request, handle);
674 break;
675
676 default:
677 rest_response_debug(request, handle);
678 break;
679 }
680
681finish:
682 rest_slab_release(handle);
683
684 RETURN_UNLANG_RCODE(rcode);
685}
686
687/*
688 * Find the named user in this modules database. Create the set
689 * of attribute-value pairs to check and reply with for this user
690 * from the database. The authentication code only needs to check
691 * the password, the rest is done here.
692 */
693static unlang_action_t CC_HINT(nonnull) mod_common(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
694{
695 rlm_rest_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_rest_thread_t);
696 rlm_rest_call_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
697 rlm_rest_section_t const *section = &env->section->section;
698
699 void *handle;
700 int ret;
701
702 handle = rest_slab_reserve(t->slab);
703 if (!handle) RETURN_UNLANG_FAIL;
704
705 ret = rlm_rest_perform(mctx, section, handle, request);
706 if (ret < 0) {
707 rest_slab_release(handle);
708
710 }
711
713}
714
716 module_ctx_t const *mctx, request_t *request)
717{
719 rlm_rest_call_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
720 rlm_rest_section_t const *section = &env->section->section;
721 fr_curl_io_request_t *handle = talloc_get_type_abort(mctx->rctx, fr_curl_io_request_t);
722
723 int hcode;
724 int rcode = RLM_MODULE_OK;
725 int ret;
726
727 if (section->tls.extract_cert_attrs) fr_curl_response_certinfo(request, handle);
728
729 if (rlm_rest_status_update(request, handle) < 0) {
730 rcode = RLM_MODULE_FAIL;
731 goto finish;
732 }
733
734 hcode = rest_get_handle_code(handle);
735 switch (hcode) {
736 case 404:
737 case 410:
738 rcode = RLM_MODULE_NOTFOUND;
739 break;
740
741 case 403:
742 rcode = RLM_MODULE_DISALLOW;
743 break;
744
745 case 401:
746 /*
747 * Attempt to parse content if there was any.
748 */
749 ret = rest_response_decode(inst, section, request, handle);
750 if (ret < 0) {
751 rcode = RLM_MODULE_FAIL;
752 break;
753 }
754
755 rcode = RLM_MODULE_REJECT;
756 break;
757
758 case 204:
759 rcode = RLM_MODULE_OK;
760 break;
761
762 default:
763 /*
764 * Attempt to parse content if there was any.
765 */
766 if ((hcode >= 200) && (hcode < 300)) {
767 ret = rest_response_decode(inst, section, request, handle);
768 if (ret < 0) rcode = RLM_MODULE_FAIL;
769 else if (ret == 0) rcode = RLM_MODULE_OK;
770 else rcode = RLM_MODULE_UPDATED;
771 break;
772 } else if (hcode < 500) {
773 rcode = RLM_MODULE_INVALID;
774 } else {
775 rcode = RLM_MODULE_FAIL;
776 }
777 }
778
779 switch (rcode) {
781 case RLM_MODULE_FAIL:
783 rest_response_error(request, handle);
784 break;
785
786 default:
787 rest_response_debug(request, handle);
788 break;
789 }
790
791finish:
792 rest_slab_release(handle);
793
794 RETURN_UNLANG_RCODE(rcode);
795}
796
797/*
798 * Authenticate the user with the given password.
799 */
800static unlang_action_t CC_HINT(nonnull) mod_authenticate(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
801{
802 rlm_rest_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_rest_thread_t);
803 rlm_rest_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
804 rlm_rest_section_t const *section = &call_env->section->section;
805 fr_curl_io_request_t *handle;
806 int ret;
807
808 /*
809 * We can only authenticate user requests which HAVE
810 * a User-Name attribute.
811 */
812 if (!call_env->request.username) {
813 REDEBUG("Attribute \"User-Name\" is required for authentication");
815 }
816
817 if (!call_env->request.password) {
818 REDEBUG("Attribute \"User-Password\" is required for authentication");
820 }
821
822 /*
823 * Make sure the supplied password isn't empty
824 */
825 if (call_env->request.password->vb_length == 0) {
826 REDEBUG("User-Password must not be empty");
828 }
829
830 /*
831 * Log the password
832 */
833 if (RDEBUG_ENABLED3) {
834 RDEBUG("Login attempt with password \"%pV\"", call_env->request.password);
835 } else {
836 RDEBUG2("Login attempt with password");
837 }
838
839 handle = rest_slab_reserve(t->slab);
840 if (!handle) RETURN_UNLANG_FAIL;
841
842 ret = rlm_rest_perform(mctx, section, handle, request);
843 if (ret < 0) {
844 rest_slab_release(handle);
845
847 }
848
850}
851
853{
855 rlm_rest_call_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
856 rlm_rest_section_t const *section = &env->section->section;
857 fr_curl_io_request_t *handle = talloc_get_type_abort(mctx->rctx, fr_curl_io_request_t);
858
859 int hcode;
860 int rcode = RLM_MODULE_OK;
861 int ret;
862
863 if (section->tls.extract_cert_attrs) fr_curl_response_certinfo(request, handle);
864
865 if (rlm_rest_status_update(request, handle) < 0) {
866 rcode = RLM_MODULE_FAIL;
867 goto finish;
868 }
869
870 hcode = rest_get_handle_code(handle);
871 if (hcode >= 500) {
872 rcode = RLM_MODULE_FAIL;
873 } else if (hcode == 204) {
874 rcode = RLM_MODULE_OK;
875 } else if ((hcode >= 200) && (hcode < 300)) {
876 ret = rest_response_decode(inst, section, request, handle);
877 if (ret < 0) rcode = RLM_MODULE_FAIL;
878 else if (ret == 0) rcode = RLM_MODULE_OK;
879 else rcode = RLM_MODULE_UPDATED;
880 } else {
881 rcode = RLM_MODULE_INVALID;
882 }
883
884 switch (rcode) {
886 case RLM_MODULE_FAIL:
887 rest_response_error(request, handle);
888 break;
889
890 default:
891 rest_response_debug(request, handle);
892 break;
893 }
894
895finish:
896 rest_slab_release(handle);
897
898 RETURN_UNLANG_RCODE(rcode);
899}
900
901/*
902 * Send accounting info to a REST API endpoint
903 */
904static unlang_action_t CC_HINT(nonnull) mod_accounting(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
905{
906 rlm_rest_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_rest_thread_t);
907 rlm_rest_call_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
908 rlm_rest_section_t const *section = &env->section->section;
909 void *handle;
910 int ret;
911
912 handle = rest_slab_reserve(t->slab);
913 if (!handle) RETURN_UNLANG_FAIL;
914
915 ret = rlm_rest_perform(mctx, section, handle, request);
916 if (ret < 0) {
917 rest_slab_release(handle);
918
920 }
921
923}
924
926 rlm_rest_section_t *config, char const *name, CONF_SECTION *cs)
927{
928 CONF_SECTION *request_cs;
929
930 if (!cs) cs = cf_section_find(parent, name, NULL);
931 if (!cs) {
932 config->name = NULL;
933 return 0;
934 }
935
936 if (cf_section_rules_push(cs, config_items) < 0) return -1;
937 if (cf_section_parse(inst, config, cs) < 0) {
938 config->name = NULL;
939 return -1;
940 }
941
942 /*
943 * Add section name (Maybe add to headers later?).
944 */
945 config->name = name;
946
947 /*
948 * Convert HTTP method auth and body type strings into their integer equivalents.
949 */
950 if ((config->request.auth != REST_HTTP_AUTH_NONE) && !http_curl_auth[config->request.auth]) {
951 cf_log_err(cs, "Unsupported HTTP auth type \"%s\", check libcurl version, OpenSSL build "
952 "configuration, then recompile this module",
953 fr_table_str_by_value(http_auth_table, config->request.auth, "<INVALID>"));
954
955 return -1;
956 }
957 config->request.method = fr_table_value_by_str(http_method_table, config->request.method_str, REST_HTTP_METHOD_CUSTOM);
958
959 /*
960 * Custom hackery to figure out if data was set we can't do it any other way because we can't
961 * parse the tmpl_t except within a call_env.
962 *
963 * We have custom body data so we set REST_HTTP_BODY_CUSTOM, but also need to try and
964 * figure out what content-type to use. So if they've used the canonical form we
965 * need to convert it back into a proper HTTP content_type value.
966 */
967 if ((strcmp(name, "xlat") == 0) || ((request_cs = cf_section_find(cs, "request", NULL)) && cf_pair_find(request_cs, "data"))) {
968 http_body_type_t body;
969
970 config->request.body = REST_HTTP_BODY_CUSTOM;
971
973 if (body != REST_HTTP_BODY_UNKNOWN) {
974 config->request.body_str = fr_table_str_by_value(http_content_type_table, body, config->request.body_str);
975 }
976 /*
977 * We don't have any custom user data, so we need to select the right encoder based
978 * on the body type.
979 *
980 * To make this slightly more/less confusing, we accept both canonical body_types,
981 * and content_types.
982 */
983 } else {
985 if (config->request.body == REST_HTTP_BODY_UNKNOWN) {
987 }
988
989 if (config->request.body == REST_HTTP_BODY_UNKNOWN) {
990 cf_log_err(cs, "Unknown HTTP body type '%s'", config->request.body_str);
991 return -1;
992 }
993
994 switch (http_body_type_supported[config->request.body]) {
996 cf_log_err(cs, "Unsupported HTTP body type \"%s\", please submit patches",
997 config->request.body_str);
998 return -1;
999
1001 cf_log_err(cs, "Invalid HTTP body type. \"%s\" is not a valid web API data "
1002 "markup format", config->request.body_str);
1003 return -1;
1004
1006 cf_log_err(cs, "Unavailable HTTP body type. \"%s\" is not available in this "
1007 "build", config->request.body_str);
1008 return -1;
1009
1010 default:
1011 break;
1012 }
1013 }
1014
1015 if (config->response.force_to_str) {
1016 config->response.force_to = fr_table_value_by_str(http_body_type_table, config->response.force_to_str, REST_HTTP_BODY_UNKNOWN);
1017 if (config->response.force_to == REST_HTTP_BODY_UNKNOWN) {
1018 config->response.force_to = fr_table_value_by_str(http_content_type_table, config->response.force_to_str, REST_HTTP_BODY_UNKNOWN);
1019 }
1020
1021 if (config->response.force_to == REST_HTTP_BODY_UNKNOWN) {
1022 cf_log_err(cs, "Unknown forced response body type '%s'", config->response.force_to_str);
1023 return -1;
1024 }
1025
1026 switch (http_body_type_supported[config->response.force_to]) {
1028 cf_log_err(cs, "Unsupported forced response body type \"%s\", please submit patches",
1029 config->response.force_to_str);
1030 return -1;
1031
1033 cf_log_err(cs, "Invalid HTTP forced response body type. \"%s\" is not a valid web API data "
1034 "markup format", config->response.force_to_str);
1035 return -1;
1036
1037 default:
1038 break;
1039 }
1040 }
1041
1042 return 0;
1043}
1044
1045/** Cleans up after a REST request.
1046 *
1047 * Resets all options associated with a CURL handle, and frees any headers
1048 * associated with it.
1049 *
1050 * @param[in] randle to cleanup.
1051 * @param[in] uctx unused.
1052 */
1054{
1055 rlm_rest_curl_context_t *ctx = talloc_get_type_abort(randle->uctx, rlm_rest_curl_context_t);
1056 CURL *candle = randle->candle;
1057
1058 /*
1059 * Clear any previously configured options
1060 */
1061 curl_easy_reset(candle);
1062
1063 /*
1064 * Free header list
1065 */
1066 if (ctx->headers != NULL) {
1067 curl_slist_free_all(ctx->headers);
1068 ctx->headers = NULL;
1069 }
1070
1071#ifndef NDEBUG
1072 {
1073 CURLcode ret;
1074 /*
1075 * With curl 7.61 when a request in cancelled we get a result
1076 * with a NULL (invalid) pointer to private data. This lets
1077 * us know that the request was returned to the slab.
1078 */
1079 ret = curl_easy_setopt(candle, CURLOPT_PRIVATE, (void *)0xdeadc341);
1080 if (unlikely(ret != CURLE_OK)) {
1081 ERROR("Failed to set private data on curl easy handle %p: %s",
1082 candle, curl_easy_strerror(ret));
1083 }
1084 }
1085#endif
1086
1087 /*
1088 * Free response data
1089 */
1090 TALLOC_FREE(ctx->body);
1091 TALLOC_FREE(ctx->response.buffer);
1092 TALLOC_FREE(ctx->request.encoder);
1093 TALLOC_FREE(ctx->response.decoder);
1094 ctx->response.header = NULL; /* This is owned by the parsed call env and must not be freed */
1095
1096 randle->request = NULL;
1097 return 0;
1098}
1099
1101{
1102 curl_easy_cleanup(randle->candle);
1103 return 0;
1104}
1105
1106static int rest_conn_alloc(fr_curl_io_request_t *randle, void *uctx)
1107{
1108 rlm_rest_t const *inst = talloc_get_type_abort(uctx, rlm_rest_t);
1109 rlm_rest_curl_context_t *curl_ctx = NULL;
1110
1111 randle->candle = curl_easy_init();
1112 if (unlikely(!randle->candle)) {
1113 fr_strerror_printf("Unable to initialise CURL handle");
1114 return -1;
1115 }
1116
1117 MEM(curl_ctx = talloc_zero(randle, rlm_rest_curl_context_t));
1118 curl_ctx->headers = NULL;
1119 curl_ctx->request.instance = inst;
1120 curl_ctx->response.instance = inst;
1121
1122 randle->uctx = curl_ctx;
1123 talloc_set_destructor(randle, _mod_conn_free);
1124
1125 rest_slab_element_set_destructor(randle, _rest_request_cleanup, NULL);
1126
1127 return 0;
1128}
1129
1130/** Create a thread specific multihandle
1131 *
1132 * Easy handles representing requests are added to the curl multihandle
1133 * with the multihandle used for mux/demux.
1134 *
1135 * @param[in] mctx Thread instantiation data.
1136 * @return
1137 * - 0 on success.
1138 * - -1 on failure.
1139 */
1141{
1142 rlm_rest_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_rest_t);
1143 rlm_rest_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_rest_thread_t);
1144 fr_curl_handle_t *mhandle;
1145
1146 t->inst = inst;
1147
1148 if (!(t->slab = rest_slab_list_alloc(t, mctx->el, &inst->conn_config.reuse,
1149 rest_conn_alloc, NULL, inst, false, false))) {
1150 ERROR("Connection handle pool instantiation failed");
1151 return -1;
1152 }
1153
1154 mhandle = fr_curl_io_init(t, mctx->el, inst->multiplex);
1155 if (!mhandle) return -1;
1156
1157 t->mhandle = mhandle;
1158
1159 return 0;
1160}
1161
1162/** Cleanup all outstanding requests associated with this thread
1163 *
1164 * Destroys all curl easy handles, and then the multihandle associated
1165 * with this thread.
1166 *
1167 * @param[in] mctx data to destroy.
1168 * @return 0
1169 */
1171{
1172 rlm_rest_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_rest_thread_t);
1173
1174 talloc_free(t->mhandle); /* Ensure this is shutdown before the pool */
1175 talloc_free(t->slab);
1176
1177 return 0;
1178}
1179
1180/*
1181 * Do any per-module initialization that is separate to each
1182 * configured instance of the module. e.g. set up connections
1183 * to external databases, read configuration files, set up
1184 * dictionary entries, etc.
1185 *
1186 * If configuration information is given in the config section
1187 * that must be referenced in later calls, store a handle to it
1188 * in *instance otherwise put a null pointer there.
1189 */
1190static int mod_instantiate(module_inst_ctx_t const *mctx)
1191{
1192 rlm_rest_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_rest_t);
1193 CONF_SECTION *conf = mctx->mi->conf;
1194 rlm_rest_section_conf_t *section;
1196
1197 inst->xlat.request.method_str = "GET";
1198 inst->xlat.request.body = REST_HTTP_BODY_NONE;
1199 inst->xlat.request.body_str = "application/x-www-form-urlencoded";
1200 inst->xlat.response.force_to_str = "plain";
1201
1202 if (!inst->sections_init) fr_rb_inline_init(&inst->sections, rlm_rest_section_conf_t, node, rest_section_cmp, NULL);
1203
1204 /*
1205 * Parse xlat config.
1206 */
1207 if ((parse_sub_section(inst, conf, xlat_config, &inst->xlat, "xlat", NULL) < 0)) return -1;
1208
1209 /*
1210 * Parse section configs from calls found by the call_env parser.
1211 */
1212 section = fr_rb_iter_init_inorder(&iter, &inst->sections);
1213 while (section) {
1215 cf_section_name(section->cs), section->cs) < 0) return -1;
1216 section = fr_rb_iter_next_inorder(&iter);
1217 }
1218
1219 inst->conn_config.reuse.num_children = 1;
1220 inst->conn_config.reuse.child_pool_size = sizeof(rlm_rest_curl_context_t);
1221
1222 return 0;
1223}
1224
1225static int mod_bootstrap(module_inst_ctx_t const *mctx)
1226{
1227 xlat_t *xlat;
1228
1229 xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, NULL, rest_xlat, FR_TYPE_STRING);
1232
1233 return 0;
1234}
1235
1236static int mod_load(void)
1237{
1238 /* developer sanity */
1240
1241#ifdef HAVE_JSON
1243#endif
1244
1245 return 0;
1246}
1247
1248/*
1249 * Custom call_env parser which looks for a conf section matching the name
1250 * of the section the module is called in and then hands off to the normal
1251 * parsing.
1252 */
1253static int rest_sect_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, UNUSED tmpl_rules_t const *t_rules,
1254 CONF_ITEM *ci, call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
1255{
1256 rlm_rest_t *inst = talloc_get_type_abort(cec->mi->data, rlm_rest_t);
1257 CONF_SECTION *cs;
1258 CONF_SECTION *sect = NULL;
1259 call_env_parsed_t *parsed;
1260 void *found;
1261 rlm_rest_section_conf_t *section;
1262 char *p, *name2 = NULL;
1263 size_t i;
1264
1265 /*
1266 * The parent section is the main module conf section
1267 * in which we'll look for a suitable section to parse.
1268 */
1269 cs = cf_item_to_section(cf_parent(ci));
1270
1271 if (cec->asked->name2) {
1272 name2 = talloc_strdup(NULL, cec->asked->name2);
1273 p = name2;
1274 for (i = 0; i < talloc_array_length(name2); i++) {
1275 *p = tolower(*p);
1276 p++;
1277 }
1278 sect = cf_section_find(cs, cec->asked->name1, name2);
1279 }
1280
1281 if (!sect) {
1282 sect = cf_section_find(cs, cec->asked->name1, NULL);
1283 }
1284
1285 if (!inst->sections_init) {
1287 inst->sections_init = true;
1288 }
1289
1290 if (!sect) {
1291 cf_log_err(cs, "%s called in %s %s - requires conf section %s %s%s%s", cec->mi->name,
1292 cec->asked->name1, cec->asked->name2 ? cec->asked->name2 : "",
1293 cec->asked->name1, cec->asked->name2 ? name2 : "",
1294 cec->asked->name2 ? " or " : "",
1295 cec->asked->name2 ? cec->asked->name1 : "");
1296 talloc_free(name2);
1297 return -1;
1298 }
1299 talloc_free(name2);
1300
1301 /*
1302 * "authenticate" sections use a different rules with defaults set for username and password
1303 */
1304 if (strcmp(cec->asked->name1, "authenticate") == 0) {
1305 call_env_parse(ctx, out, cec->mi->name, t_rules, sect, cec, rest_section_authenticate_env);
1306 } else {
1307 call_env_parse(ctx, out, cec->mi->name, t_rules, sect, cec, rest_section_common_env);
1308 }
1309 parsed = call_env_parsed_add(ctx, out,
1311 .name = "section",
1312 .flags = CALL_ENV_FLAG_PARSE_ONLY,
1313 .pair = {
1314 .parsed = {
1315 .offset = offsetof(rlm_rest_call_env_t, section),
1317 }
1318 }
1319 });
1320
1321 MEM(section = talloc_zero(inst, rlm_rest_section_conf_t));
1322 section->cs = sect;
1323 if (fr_rb_find_or_insert(&found, &inst->sections, section) < 0) {
1324 talloc_free(section);
1325 return -1;
1326 }
1327 if (found) {
1328 talloc_free(section);
1329 call_env_parsed_set_data(parsed, found);
1330 } else {
1331 call_env_parsed_set_data(parsed, section);
1332 }
1333 return 0;
1334};
1335
1343
1344/*
1345 * The module name should be the only globally exported symbol.
1346 * That is, everything else should be 'static'.
1347 *
1348 * If the module needs to temporarily modify it's instantiation
1349 * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
1350 * The server will then take care of ensuring that the module
1351 * is single-threaded.
1352 */
1353extern module_rlm_t rlm_rest;
1355 .common = {
1356 .magic = MODULE_MAGIC_INIT,
1357 .name = "rest",
1358 .inst_size = sizeof(rlm_rest_t),
1359 .thread_inst_size = sizeof(rlm_rest_thread_t),
1360 .config = module_config,
1361 .onload = mod_load,
1362 .bootstrap = mod_bootstrap,
1363 .instantiate = mod_instantiate,
1364 .thread_instantiate = mod_thread_instantiate,
1365 .thread_detach = mod_thread_detach
1366 },
1367 .method_group = {
1368 .bindings = (module_method_binding_t[]){
1369 { .section = SECTION_NAME("recv", "accounting-request"), .method = mod_accounting, .method_env = &rest_method_env },
1370 { .section = SECTION_NAME("accounting", CF_IDENT_ANY), .method = mod_accounting, .method_env = &rest_method_env },
1371 { .section = SECTION_NAME("authenticate", CF_IDENT_ANY), .method = mod_authenticate, .method_env = &rest_method_env },
1372 { .section = SECTION_NAME("send", CF_IDENT_ANY), .method = mod_accounting, .method_env = &rest_method_env },
1373 { .section = SECTION_NAME(CF_IDENT_ANY, CF_IDENT_ANY), .method = mod_common, .method_env = &rest_method_env },
1375 }
1376 }
1377};
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:1438
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:1593
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:289
#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:274
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:287
static fr_slen_t in
Definition dict.h:841
Specifies an attribute which must be present for the module to function.
Definition dict.h:273
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:286
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:492
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
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:1236
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:1106
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:925
static unlang_action_t mod_common(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_rest.c:693
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:1225
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:904
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:1053
#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:1336
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:852
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:1140
static unlang_action_t mod_authenticate(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_rest.c:800
module_rlm_t rlm_rest
Definition rlm_rest.c:1354
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:1100
static xlat_arg_parser_t const rest_xlat_args[]
Definition rlm_rest.c:475
static unlang_action_t mod_authenticate_result(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_rest.c:715
static int mod_thread_detach(module_thread_inst_ctx_t const *mctx)
Cleanup all outstanding requests associated with this thread.
Definition rlm_rest.c:1170
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition rlm_rest.c:1190
static unlang_action_t mod_common_result(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_rest.c:606
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:1253
#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:4402
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:4498
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:6135
@ FR_VALUE_BOX_LIST_FREE
Definition value.h:238
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:653
#define fr_value_box_list_foreach(_list_head, _iter)
Definition value.h:223
static size_t char ** out
Definition value.h:1022
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