The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
rest.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: d958bb07a6f46a44c8a0e548a10198c3c32b4cdf $
19 *
20 * @brief Functions and datatypes for the REST (HTTP) transport.
21 * @file rest.c
22 *
23 * @copyright 2012-2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24 */
25RCSID("$Id: d958bb07a6f46a44c8a0e548a10198c3c32b4cdf $")
26
27#define LOG_PREFIX mctx->mi->name
28
29#include <ctype.h>
30#include <string.h>
31
32#include <freeradius-devel/util/skip.h>
33
34
35#include "rest.h"
36
37/** Table of encoder/decoder support.
38 *
39 * Indexes in this table match the http_body_type_t enum, and should be
40 * updated if additional enum values are added.
41 *
42 * @see http_body_type_t
43 */
45 REST_HTTP_BODY_UNKNOWN, // REST_HTTP_BODY_UNKNOWN
46 REST_HTTP_BODY_UNSUPPORTED, // REST_HTTP_BODY_UNSUPPORTED
47 REST_HTTP_BODY_UNSUPPORTED, // REST_HTTP_BODY_UNAVAILABLE
48 REST_HTTP_BODY_UNSUPPORTED, // REST_HTTP_BODY_INVALID
49 REST_HTTP_BODY_NONE, // REST_HTTP_BODY_NONE
50 REST_HTTP_BODY_CUSTOM, // REST_HTTP_BODY_CUSTOM
51 REST_HTTP_BODY_POST, // REST_HTTP_BODY_POST
52#ifdef HAVE_JSON
53 REST_HTTP_BODY_JSON, // REST_HTTP_BODY_JSON
54#else
56#endif
57 REST_HTTP_BODY_UNSUPPORTED, // REST_HTTP_BODY_XML
58 REST_HTTP_BODY_UNSUPPORTED, // REST_HTTP_BODY_YAML
59 REST_HTTP_BODY_INVALID, // REST_HTTP_BODY_HTML
60 REST_HTTP_BODY_PLAIN, // REST_HTTP_BODY_PLAIN
61 REST_HTTP_BODY_PLAIN // REST_HTTP_BODY_CRL
62};
63
64/** Table of which known body types are expected to give binary data
65 */
70
71/*
72 * Lib CURL doesn't define symbols for unsupported auth methods
73 */
74#ifndef CURLOPT_TLSAUTH_SRP
75# define CURLOPT_TLSAUTH_SRP 0
76#endif
77#ifndef CURLAUTH_BASIC
78# define CURLAUTH_BASIC 0
79#endif
80#ifndef CURLAUTH_DIGEST
81# define CURLAUTH_DIGEST 0
82#endif
83#ifndef CURLAUTH_DIGEST_IE
84# define CURLAUTH_DIGEST_IE 0
85#endif
86#ifndef CURLAUTH_GSSNEGOTIATE
87# define CURLAUTH_GSSNEGOTIATE 0
88#endif
89#ifndef CURLAUTH_NTLM
90# define CURLAUTH_NTLM 0
91#endif
92#ifndef CURLAUTH_NTLM_WB
93# define CURLAUTH_NTLM_WB 0
94#endif
95
96/*
97 * CURL headers do:
98 *
99 * #define curl_easy_setopt(handle,opt,param) curl_easy_setopt(handle,opt,param)
100 */
114
115/** Conversion table for method config values.
116 *
117 * HTTP verb strings for http_method_t enum values. Used by libcurl in the
118 * status line of the outgoing HTTP header, by rest_response_header for decoding
119 * incoming HTTP responses, and by the configuration parser.
120 *
121 * @note must be kept in sync with http_method_t enum.
122 *
123 * @see http_method_t
124 * @see fr_table_value_by_str
125 * @see fr_table_str_by_value
126 */
128 { L("DELETE"), REST_HTTP_METHOD_DELETE },
129 { L("GET"), REST_HTTP_METHOD_GET },
130 { L("PATCH"), REST_HTTP_METHOD_PATCH },
131 { L("POST"), REST_HTTP_METHOD_POST },
132 { L("PUT"), REST_HTTP_METHOD_PUT },
133 { L("UNKNOWN"), REST_HTTP_METHOD_UNKNOWN }
134};
136
137/** Conversion table for type config values.
138 *
139 * Textual names for http_body_type_t enum values, used by the
140 * configuration parser.
141 *
142 * @see http_body_Type_t
143 * @see fr_table_value_by_str
144 * @see fr_table_str_by_value
145 */
147 { L("crl"), REST_HTTP_BODY_CRL },
148 { L("html"), REST_HTTP_BODY_HTML },
149 { L("invalid"), REST_HTTP_BODY_INVALID },
150 { L("json"), REST_HTTP_BODY_JSON },
151 { L("none"), REST_HTTP_BODY_NONE },
152 { L("plain"), REST_HTTP_BODY_PLAIN },
153 { L("post"), REST_HTTP_BODY_POST },
154 { L("unavailable"), REST_HTTP_BODY_UNAVAILABLE },
155 { L("unknown"), REST_HTTP_BODY_UNKNOWN },
156 { L("unsupported"), REST_HTTP_BODY_UNSUPPORTED },
157 { L("xml"), REST_HTTP_BODY_XML },
158 { L("yaml"), REST_HTTP_BODY_YAML }
159};
161
163 { L("any"), REST_HTTP_AUTH_ANY },
164 { L("basic"), REST_HTTP_AUTH_BASIC },
165 { L("digest"), REST_HTTP_AUTH_DIGEST },
166 { L("digest-ie"), REST_HTTP_AUTH_DIGEST_IE },
167 { L("gss-negotiate"), REST_HTTP_AUTH_GSSNEGOTIATE },
168 { L("none"), REST_HTTP_AUTH_NONE },
169 { L("ntlm"), REST_HTTP_AUTH_NTLM },
170 { L("ntlm-winbind"), REST_HTTP_AUTH_NTLM_WB },
171 { L("safe"), REST_HTTP_AUTH_ANY_SAFE },
172 { L("srp"), REST_HTTP_AUTH_TLS_SRP }
173};
175
176/** Conversion table for "Content-Type" header values.
177 *
178 * Used by rest_response_header for parsing incoming headers.
179 *
180 * Values we expect to see in the 'Content-Type:' header of the incoming
181 * response.
182 *
183 * Some data types (like YAML) do no have standard MIME types defined,
184 * so multiple types, are listed here.
185 *
186 * @see http_body_Type_t
187 * @see fr_table_value_by_str
188 * @see fr_table_str_by_value
189 */
191 { L("application/json"), REST_HTTP_BODY_JSON },
192 { L("application/pkix-crl"), REST_HTTP_BODY_CRL },
193 { L("application/x-pkcs7-crl"), REST_HTTP_BODY_CRL },
194 { L("application/x-www-form-urlencoded"), REST_HTTP_BODY_POST },
195 { L("application/x-yaml"), REST_HTTP_BODY_YAML },
196 { L("application/yaml"), REST_HTTP_BODY_YAML },
197 { L("text/html"), REST_HTTP_BODY_HTML },
198 { L("text/plain"), REST_HTTP_BODY_PLAIN },
199 { L("text/x-yaml"), REST_HTTP_BODY_YAML },
200 { L("text/xml"), REST_HTTP_BODY_XML },
201 { L("text/yaml"), REST_HTTP_BODY_YAML }
202};
204
205/*
206 * Encoder specific structures.
207 * @todo split encoders/decoders into submodules.
208 */
209typedef struct {
210 char const *start; //!< Start of the buffer.
211 char const *p; //!< how much text we've sent so far.
212 size_t len; //!< Length of data
214
215#ifdef HAVE_JSON
216/** Flags to control the conversion of JSON values to fr_pair_ts.
217 *
218 * These fields are set when parsing the expanded format for value pairs in
219 * JSON, and control how json_pair_alloc_leaf and json_pair_alloc convert the JSON
220 * value, and move the new fr_pair_t into an attribute list.
221 *
222 * @see json_pair_alloc
223 * @see json_pair_alloc_leaf
224 */
225typedef struct {
226 int do_xlat; //!< If true value will be expanded with xlat.
227 int is_json; //!< If true value will be inserted as raw JSON
228 ///< (multiple values not supported).
229 json_object *value; //!< Object to extract value from
231#endif
232
233/** Frees a libcurl handle, and any additional memory used by context data.
234 *
235 * @param[in] randle fr_curl_io_request_t to close and free.
236 * @return returns true.
237 */
239{
240 curl_easy_cleanup(randle->candle);
241
242 return 0;
243}
244
245/** Creates a new connection handle for use by the FR connection API.
246 *
247 * Matches the fr_pool_connection_create_t function prototype, is passed to
248 * fr_pool_init, and called when a new connection is required by the
249 * connection pool API.
250 *
251 * Creates an instances of fr_curl_io_request_t, and rlm_rest_curl_context_t
252 * which hold the context data required for generating requests and parsing
253 * responses.
254 *
255 * @see fr_pool_init
256 * @see fr_pool_connection_create_t
257 * @see connection.c
258 */
259void *rest_mod_conn_create(TALLOC_CTX *ctx, void *instance, UNUSED fr_time_delta_t timeout)
260{
262
263 fr_curl_io_request_t *randle = NULL;
264 rlm_rest_curl_context_t *curl_ctx = NULL;
265
266 /*
267 * Allocate memory for the connection handle abstraction.
268 */
269 randle = fr_curl_io_request_alloc(ctx);
270 if (!randle) return NULL;
271
272 MEM(curl_ctx = talloc_zero(randle, rlm_rest_curl_context_t));
273
274 curl_ctx->headers = NULL; /* CURL needs this to be NULL */
275 curl_ctx->request.instance = inst;
276 curl_ctx->response.instance = inst;
277
278 randle->uctx = curl_ctx;
279 talloc_set_destructor(randle, _mod_conn_free);
280
281 return randle;
282}
283
284/** Copies a pre-expanded xlat string to the output buffer
285 *
286 * @param[out] out Char buffer to write encoded data to.
287 * @param[in] size Multiply by nmemb to get the length of ptr.
288 * @param[in] nmemb Multiply by size to get the length of ptr.
289 * @param[in] userdata rlm_rest_request_t to keep encoding state between calls.
290 * @return
291 * - Length of data (including NULL) written to ptr.
292 * - 0 if no more data to write.
293 */
294static size_t rest_encode_custom(void *out, size_t size, size_t nmemb, void *userdata)
295{
296 rlm_rest_request_t *ctx = userdata;
298
299 size_t freespace = (size * nmemb) - 1;
300 size_t len;
301 size_t to_copy;
302
303 /*
304 * Special case for empty body
305 */
306 if (data->len == 0) return 0;
307
308 /*
309 * If len > 0 then we must have these set.
310 */
311 fr_assert(data->start);
312 fr_assert(data->p);
313
314 to_copy = data->len - (data->p - data->start);
315 len = to_copy > freespace ? freespace : to_copy;
316 if (len == 0) return 0;
317
318 memcpy(out, data->p, len);
319 data->p += len;
320
321 return len;
322}
323
324/** Encodes fr_pair_t linked list in POST format
325 *
326 * This is a stream function matching the rest_read_t prototype. Multiple
327 * successive calls will return additional encoded fr_pair_ts.
328 * Only complete attribute headers @verbatim '<name>=' @endverbatim and values
329 * will be written to the ptr buffer.
330 *
331 * POST request format is:
332 * @verbatim <attribute0>=<value0>&<attribute1>=<value1>&<attributeN>=<valueN>@endverbatim
333 *
334 * All attributes and values are url encoded. There is currently no support for
335 * nested attributes, or attribute qualifiers.
336 *
337 * Nested attributes may be added in the future using
338 * @verbatim <attribute-outer>:<attribute-inner>@endverbatim
339 * to denotate nesting.
340 *
341 * Requires libcurl for url encoding.
342 *
343 * @see rest_decode_post
344 *
345 * @param[out] out Char buffer to write encoded data to.
346 * @param[in] size Multiply by nmemb to get the length of ptr.
347 * @param[in] nmemb Multiply by size to get the length of ptr.
348 * @param[in] userdata rlm_rest_request_t to keep encoding state between calls.
349 * @return
350 * - Length of data (including NULL) written to ptr.
351 * - 0 if no more data to write.
352 */
353static size_t rest_encode_post(void *out, size_t size, size_t nmemb, void *userdata)
354{
355 rlm_rest_request_t *ctx = userdata;
356 request_t *request = ctx->request; /* Used by RDEBUG */
357 fr_pair_t *vp;
358
359 size_t len = 0;
360 ssize_t slen;
361 size_t freespace = (size * nmemb) - 1;
362
363 char *p = out; /* Position in buffer */
364 char *encoded = p; /* Position in buffer of last fully encoded attribute or value */
365 char *escaped; /* Pointer to current URL escaped data */
366
367 /* Allow manual chunking */
368 if ((ctx->chunk) && (ctx->chunk <= freespace)) freespace = (ctx->chunk - 1);
369
370 if (ctx->state == READ_STATE_END) return 0;
371
372 /* Post data requires no headers */
374
375 while (freespace > 0) {
377 if (!vp) {
378 ctx->state = READ_STATE_END;
379
380 break;
381 }
382
383 RDEBUG2("Encoding attribute \"%s\"", vp->da->name);
384
385 if (ctx->state == READ_STATE_ATTR_BEGIN) {
386 escaped = curl_escape(vp->da->name, 0);
387 if (!escaped) {
388 REDEBUG("Failed escaping string \"%s\"", vp->da->name);
389 return 0;
390 }
391
392 len = strlen(escaped);
393 if (freespace < (1 + len)) {
394 curl_free(escaped);
395 /*
396 * Cleanup for error conditions
397 */
398 no_space:
399 *encoded = '\0';
400
401 len = encoded - (char *)out;
402
403 RDEBUG3("POST Data: %pV", fr_box_strvalue_len(out, len));
404
405 /*
406 * The buffer wasn't big enough to encode a single attribute chunk.
407 */
408 if (len == 0) {
409 REDEBUG("Failed encoding attribute");
410 } else {
411 RDEBUG3("Returning %zu bytes of POST data "
412 "(buffer full or chunk exceeded)", len);
413 }
414
415 return len;
416 }
417
418 len = snprintf(p, freespace, "%s=", escaped);
419 curl_free(escaped);
420 p += len;
421 freespace -= len;
422
423 /*
424 * We wrote the attribute header, record progress.
425 */
426 encoded = p;
428 }
429
430 /*
431 * Write out single attribute string.
432 */
433 slen = fr_pair_print_value_quoted(&FR_SBUFF_OUT(p, freespace), vp, T_BARE_WORD);
434 if (slen < 0) return 0;
435
436 RINDENT();
437 RDEBUG3("Length : %zu", (size_t)slen);
438 REXDENT();
439 if (slen > 0) {
440 escaped = curl_escape(p, (int)slen);
441 if (!escaped) {
442 REDEBUG("Failed escaping string \"%s\"", vp->da->name);
443 return 0;
444 }
445 len = strlen(escaped);
446
447 if (freespace < len) {
448 curl_free(escaped);
449 goto no_space;
450 }
451
452 len = strlcpy(p, escaped, len + 1);
453
454 curl_free(escaped);
455
456 RINDENT();
457 RDEBUG3("Value : %s", p);
458 REXDENT();
459
460 p += len;
461 freespace -= len;
462 }
463
464 /*
465 * there are more attributes, insert a separator
466 */
467 if (fr_dcursor_next(&ctx->cursor)) {
468 if (freespace < 1) goto no_space;
469 *p++ = '&';
470 freespace--;
471 }
472
473 /*
474 * We wrote one full attribute value pair, record progress.
475 */
476 encoded = p;
477
479 }
480
481 *p = '\0';
482
483 len = p - (char *)out;
484
485 RDEBUG3("POST Data: %s", (char *)out);
486 RDEBUG3("Returning %zu bytes of POST data", len);
487
488 return len;
489}
490
491#ifdef HAVE_JSON
492/** Encodes fr_pair_t linked list in JSON format
493 *
494 * This is a stream function matching the rest_read_t prototype. Multiple
495 * successive calls will return additional encoded fr_pair_ts.
496 *
497 * Only complete attribute headers
498 * @verbatim "<name>":{"type":"<type>","value":[' @endverbatim
499 * and complete attribute values will be written to ptr.
500 *
501 * If an attribute occurs multiple times in the request the attribute values
502 * will be concatenated into a single value array.
503 *
504 * JSON request format is:
505@verbatim
506{
507 "<attribute0>":{
508 "type":"<type0>",
509 "value":[<value0>,<value1>,<valueN>]
510 },
511 "<attribute1>":{
512 "type":"<type1>",
513 "value":[...]
514 },
515 "<attributeN>":{
516 "type":"<typeN>",
517 "value":[...]
518 },
519}
520@endverbatim
521 *
522 * @param[out] out Char buffer to write encoded data to.
523 * @param[in] size Multiply by nmemb to get the length of ptr.
524 * @param[in] nmemb Multiply by size to get the length of ptr.
525 * @param[in] userdata rlm_rest_request_t to keep encoding state between calls.
526 * @return
527 * - Length of data (including NULL) written to ptr.
528 * - 0 if no more data to write.
529 */
530static size_t rest_encode_json(void *out, size_t size, size_t nmemb, void *userdata)
531{
532 rlm_rest_request_t *ctx = userdata;
533 request_t *request = ctx->request;
535
536 size_t freespace = (size * nmemb) - 1; /* account for the \0 byte here */
537 size_t len;
538 size_t to_copy;
539 const char *encoded;
540
541 fr_assert(freespace > 0);
542
543 if (ctx->state == READ_STATE_INIT) {
544 encoded = fr_json_afrom_pair_list(data, &request->request_pairs, NULL);
545 if (!encoded) return -1;
546
547 data->start = data->p = encoded;
548 data->len = strlen(encoded);
549
550 RDEBUG3("JSON Data: %s", encoded);
551 RDEBUG3("Returning %zu bytes of JSON data", data->len);
552
554 }
555
556 to_copy = data->len - (data->p - data->start);
557 len = to_copy > freespace ? freespace : to_copy;
558
559 if (len == 0) return 0;
560
561 memcpy(out, data->p, len);
562 data->p += len;
563 return len;
564}
565#endif
566
567/** Emulates successive libcurl calls to an encoding function
568 *
569 * This function is used when the request will be sent to the HTTP server as one
570 * contiguous entity. A buffer of REST_BODY_ALLOC_CHUNK bytes is allocated and passed
571 * to the stream encoding function.
572 *
573 * If the stream function does not return 0, a new buffer is allocated which is
574 * the size of the previous buffer + REST_BODY_ALLOC_CHUNK bytes, the data from the
575 * previous buffer is copied, and freed, and another call is made to the stream
576 * function, passing a pointer into the new buffer at the end of the previously
577 * written data.
578 *
579 * This process continues until the stream function signals (by returning 0)
580 * that it has no more data to write.
581 *
582 * @param[out] out where the pointer to the alloced buffer should
583 * be written.
584 * @param[in] inst of rlm_rest.
585 * @param[in] func Stream function.
586 * @param[in] limit Maximum buffer size to alloc.
587 * @param[in] userdata rlm_rest_request_t to keep encoding state between calls to
588 * stream function.
589 * @return
590 * - Length of the data written to the buffer (excluding NULL).
591 * - -1 if alloc >= limit.
592 */
594 rest_read_t func, size_t limit, void *userdata)
595{
596 char *buff = NULL;
597 size_t needed = REST_BODY_ALLOC_CHUNK; /* Size of buffer to alloc */
598 size_t used = 0; /* Size of data written */
599 size_t len = 0;
600
601 buff = talloc_array(NULL, char, needed);
602 for (;;) {
603 len = func(buff + used, needed - used, 1, userdata);
604 used += len;
605 if (!len) {
606 *out = buff;
607 return used;
608 }
609
610 needed *= 2;
611 if (needed > limit) break;
612
613 MEM(buff = talloc_realloc(NULL, buff, char, needed));
614 }
615
617
618 return -1;
619}
620
621/** (Re-)Initialises the data in a rlm_rest_request_t.
622 *
623 * Resets the values of a rlm_rest_request_t to their defaults.
624 *
625 * @param[in] section configuration data.
626 * @param[in] request Current request.
627 * @param[in] ctx to initialise.
628 */
629static void rest_request_init(rlm_rest_section_t const *section,
631{
632 /*
633 * Setup stream read data
634 */
635 ctx->section = section;
636 ctx->request = request;
637 ctx->state = READ_STATE_INIT;
638}
639
640/** Converts plain response into a single fr_pair_t
641 *
642 * @param[in] inst configuration data.
643 * @param[in] section configuration data.
644 * @param[in] randle fr_curl_io_request_t to use.
645 * @param[in] request Current request.
646 * @param[in] raw buffer containing POST data.
647 * @param[in] rawlen Length of data in raw buffer.
648 * @return
649 * - Number of fr_pair_t processed.
650 * - -1 on unrecoverable error.
651 */
653 request_t *request, UNUSED fr_curl_io_request_t *randle, char *raw, size_t rawlen)
654{
655 fr_pair_t *vp;
656
657 /*
658 * Empty response?
659 */
660 if (*raw == '\0') return 0;
661
662 /*
663 * Use rawlen to protect against overrun, and to cope with any binary data
664 */
666 fr_pair_value_bstrndup(vp, raw, rawlen, true);
667
668 RDEBUG2("%pP", vp);
669
670 return 1;
671}
672
673/** Converts POST response into fr_pair_ts and adds them to the request
674 *
675 * Accepts fr_pair_tS in the same format as rest_encode_post, but with the
676 * addition of optional attribute list qualifiers as part of the attribute name
677 * string.
678 *
679 * If no qualifiers are specified, will default to the request list.
680 *
681 * POST response format is:
682 * @verbatim [outer.][<list>.]<attribute0>=<value0>&[outer.][<list>.]<attribute1>=<value1>&[outer.][<list>.]<attributeN>=<valueN> @endverbatim
683 *
684 * @see rest_encode_post
685 *
686 * @param[in] instance configuration data.
687 * @param[in] section configuration data.
688 * @param[in] randle fr_curl_io_request_t to use.
689 * @param[in] request Current request.
690 * @param[in] raw buffer containing POST data.
691 * @param[in] rawlen Length of data in raw buffer.
692 * @return
693 * - Number of fr_pair_ts processed.
694 * - -1 on unrecoverable error.
695 */
696static int rest_decode_post(UNUSED rlm_rest_t const *instance, rlm_rest_section_t const *section,
697 request_t *request, fr_curl_io_request_t *randle, char *raw, size_t rawlen)
698{
699 CURL *candle = randle->candle;
700
701 char const *p = raw, *q;
702
703 int count = 0;
704 int ret;
705
706 /*
707 * Empty response?
708 */
710 if (*p == '\0') return 0;
711
712 while (((q = strchr(p, '=')) != NULL) && (count < REST_BODY_MAX_ATTRS)) {
713 tmpl_t *dst;
714 request_t *current;
715 fr_pair_list_t *vps;
716 TALLOC_CTX *ctx;
717 fr_dict_attr_t const *da;
718 fr_pair_t *vp;
719
720 char *name = NULL;
721 char *value = NULL;
722
723 char const *to_parse = NULL;
724 char *expanded = NULL;
725
726 size_t len;
727 int curl_len; /* Length from last curl_easy_unescape call */
728
729 current = request;
730
731 name = curl_easy_unescape(candle, p, (q - p), &curl_len);
732 p = (q + 1);
733
734 /*
735 * Resolve attribute name to a dictionary entry and pairlist.
736 */
737 RDEBUG2("Parsing attribute \"%pV\"", fr_box_strvalue_len(name, curl_len));
738
739 if (tmpl_afrom_attr_str(request, NULL, &dst, name,
740 &(tmpl_rules_t){
741 .attr = {
742 .dict_def = request->local_dict,
743 .list_def = request_attr_reply
744 }
745 }) <= 0) {
746 RPWDEBUG("Failed parsing attribute (skipping)");
747 talloc_free(dst);
748 goto skip;
749 }
750
751 if (tmpl_request_ptr(&current, tmpl_request(dst)) < 0) {
752 RWDEBUG("Attribute name refers to outer request but not in a tunnel (skipping)");
753 talloc_free(dst);
754 goto skip;
755 }
756
757 vps = tmpl_list_head(current, tmpl_list(dst));
758 if (!vps) {
759 RWDEBUG("List not valid in this context (skipping)");
760 talloc_free(dst);
761 goto skip;
762 }
763 ctx = tmpl_list_ctx(current, tmpl_list(dst));
764 da = tmpl_attr_tail_da(dst);
765
766 fr_assert(vps);
767
768 RINDENT();
769 RDEBUG3("Type : %s", fr_type_to_str(da->type));
770
771 q = strchr(p, '&');
772 len = (!q) ? (rawlen - (p - raw)) : (unsigned)(q - p);
773
774 value = curl_easy_unescape(candle, p, (int)len, &curl_len);
775
776 /*
777 * If we found a delimiter we want to skip over it,
778 * if we didn't we do *NOT* want to skip over the end
779 * of the buffer...
780 */
781 p += (!q) ? len : (len + 1);
782
783 RDEBUG3("Length : %i", curl_len);
784 RDEBUG3("Value : \"%s\"", value);
785 REXDENT();
786
787 talloc_free(dst); /* Free our temporary tmpl */
788
789 if (section->response.post.do_xlat) {
790 RDEBUG2("Performing xlat expansion of response value");
791
792 if (xlat_aeval(request, &expanded, request, value, NULL, NULL) < 0) goto skip;
793
794 fr_assert(expanded);
795 to_parse = expanded;
796 } else {
797 to_parse = value;
798 }
799
800 MEM(vp = fr_pair_afrom_da(ctx, da));
801 if (!vp) {
802 REDEBUG("Failed creating valuepair");
803 talloc_free(expanded);
804
805 curl_free(name);
806 curl_free(value);
807
808 return count;
809 }
810
811 ret = fr_pair_value_from_str(vp, to_parse, strlen(to_parse), NULL, true);
812 TALLOC_FREE(expanded);
813 if (ret < 0) {
814 RWDEBUG("Incompatible value assignment, skipping");
816 goto skip;
817 }
818
819 fr_pair_append(vps, vp);
820
821 count++;
822
823 skip:
824 curl_free(name);
825 curl_free(value);
826
827 continue;
828 }
829
830 if (!count) REDEBUG("Malformed POST data \"%s\"", raw);
831
832 return count;
833
834}
835
836#ifdef HAVE_JSON
837/** Converts JSON "value" key into fr_pair_t when processing a map_t
838 *
839 * If leaf is not in fact a leaf node, but contains JSON data, the data will
840 * written to the attribute in JSON string format.
841 *
842 * @param[in] ctx to allocate new fr_pair_ts in.
843 * @param[in] out List to append new pair to.
844 * @param[in] request Current request.
845 * @param[in] map being processed.
846 * @param[in] uctx passed to map_tp_request.
847 * @return
848 * - 0 on success/
849 * - -1 on error.
850 */
851static int json_map_getvalue(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map, void *uctx)
852{
853 fr_dict_attr_t const *da = tmpl_attr_tail_da(map->lhs);
854 json_value_ctx_t *value_ctx = (json_value_ctx_t *) uctx;
855 char const *value;
856 char *expanded = NULL;
857 int ret = 0;
858 fr_pair_t *vp;
860
861 if (json_object_is_type(value_ctx->value, json_type_null)) {
862 RDEBUG3("Got null value for attribute \"%s\" (skipping)", da->name);
863 return -1;
864 }
865
866 MEM(vp = fr_pair_afrom_da(ctx, da));
868
869 switch (json_object_get_type(value_ctx->value)) {
870 case json_type_int:
871 if (value_ctx->do_xlat) RWDEBUG("Ignoring do_xlat on 'int', attribute \"%s\"", da->name);
872 fr_value_box(&src, (int32_t)json_object_get_int(value_ctx->value), true);
873 break;
874
875 case json_type_double:
876 if (value_ctx->do_xlat) RWDEBUG("Ignoring do_xlat on 'double', attribute \"%s\"", da->name);
877 fr_value_box(&src, (double)json_object_get_double(value_ctx->value), true);
878 break;
879
880 case json_type_string:
881 value = json_object_get_string(value_ctx->value);
882 if (value_ctx->do_xlat && memchr(value, '%', json_object_get_string_len(value_ctx->value))) {
883 if (xlat_aeval(request, &expanded, request, value, NULL, NULL) < 0) {
885 return -1;
886 }
887 fr_value_box_bstrndup_shallow(&src, NULL, expanded,
888 talloc_strlen(expanded), true);
889 } else {
890 if (fr_value_box_from_str(vp, &vp->data, da->type, da, value,
891 json_object_get_string_len(value_ctx->value), NULL) < 0) ret = -1;
892 goto finish;
893 }
894 break;
895
896 default:
897 {
898 char const *str;
899 if (value_ctx->do_xlat) RWDEBUG("Ignoring do_xlat on 'object', attribute \"%s\"", da->name);
900
901 /*
902 * Should encode any nested JSON structures into JSON strings.
903 *
904 * "I knew you liked JSON so I put JSON in your JSON!"
905 */
906 str = json_object_get_string(value_ctx->value);
907 if (!str) {
908 RWDEBUG("Failed getting string value for attribute \"%s\" (skipping)", da->name);
910 return -1;
911 }
912 fr_value_box_strdup_shallow(&src, NULL, str, true);
913 }
914 }
915
916 ret = fr_value_box_cast(vp, &vp->data, da->type, da, &src);
917finish:
918 talloc_free(expanded);
919 if (ret < 0) {
920 RWDEBUG("Failed parsing value for attribute \"%s\" (skipping)", da->name);
922 return -1;
923 }
924
926
927 return 0;
928}
929
930/** Processes JSON response and converts it into multiple fr_pair_ts
931 *
932 * Processes JSON attribute declarations in the format below. Will recurse when
933 * processing nested attributes. When processing nested attributes flags and
934 * operators from previous attributes are not inherited.
935 *
936 * JSON response format is:
937@verbatim
938{
939 "<attribute0>":{
940 "do_xlat":<bool>,
941 "is_json":<bool>,
942 "op":"<operator>",
943 "value":[<value0>,<value1>,<valueN>]
944 },
945 "<attribute1>":{
946 "value":{
947 "<nested-attribute0>":{
948 "op":"<operator>",
949 "value":<value0>
950 }
951 }
952 },
953 "<attribute2>":"<value0>",
954 "<attributeN>":[<value0>,<value1>,<valueN>]
955}
956@endverbatim
957 *
958 * JSON valuepair flags:
959 * - do_xlat (optional) Controls xlat expansion of values. Defaults to the
960 * "do_xlat" config item in the response "json"
961 * subsection, which itself defaults to false.
962 * - is_json (optional) If true, any nested JSON data will be copied to the
963 * fr_pair_t in string form. Defaults to the "is_json"
964 * config item in the response "json" subsection, which
965 * itself defaults to false.
966 * - op (optional) Controls how the attribute is inserted into
967 * the target list. Defaults to ':=' (T_OP_SET).
968 *
969 * If "op" is ':=' or '=', it will be automagically changed to '+=' for the
970 * second and subsequent values in multivalued attributes. This does not work
971 * between multiple attribute declarations.
972 *
973 * @see fr_tokens_table
974 *
975 * @param[in] instance configuration data.
976 * @param[in] section configuration data.
977 * @param[in] request Current request.
978 * @param[in] object containing root node, or parent node.
979 * @param[in] level Current nesting level.
980 * @param[in] max counter, decremented after each fr_pair_t is created,
981 * when 0 no more attributes will be processed.
982 * @return
983 * - Number of attributes created.
984 * - < 0 on error.
985 */
986static int json_pair_alloc(UNUSED rlm_rest_t const *instance, rlm_rest_section_t const *section,
987 request_t *request, json_object *object, UNUSED int level, int max)
988{
989 int max_attrs = max;
990 map_t *map = NULL;
991
992 if (!json_object_is_type(object, json_type_object)) {
993#ifdef HAVE_JSON_TYPE_TO_NAME
994 REDEBUG("Can't process VP container, expected JSON object"
995 "got \"%s\" (skipping)",
996 json_type_to_name(json_object_get_type(object)));
997#else
998 REDEBUG("Can't process VP container, expected JSON object"
999 " (skipping)");
1000#endif
1001 return -1;
1002 }
1003
1004 /*
1005 * Process VP container
1006 */
1007 {
1008 json_object_object_foreach(object, name, value) {
1009 size_t i = 0, elements;
1010 struct json_object *element, *tmp;
1011
1012 json_value_ctx_t value_ctx = {
1013 .do_xlat = section->response.json.do_xlat,
1014 .is_json = section->response.json.is_json
1015 };
1016
1017 request_t *current = request;
1018
1019 TALLOC_FREE(map);
1020
1021 /*
1022 * Resolve attribute name to a dictionary entry and pairlist.
1023 */
1024 RDEBUG2("Parsing attribute \"%s\"", name);
1025
1026 map = talloc_zero(NULL, map_t);
1027 map_list_init(&map->child);
1028 map->op = T_OP_SET;
1029
1030 if (tmpl_afrom_attr_str(request, NULL, &map->lhs, name,
1031 &(tmpl_rules_t){
1032 .attr = {
1033 .dict_def = request->local_dict,
1034 .list_def = request_attr_reply
1035 }
1036 }) <= 0) {
1037 RPWDEBUG("Failed parsing attribute (skipping)");
1038 continue;
1039 }
1040
1041 if (tmpl_request_ptr(&current, tmpl_request(map->lhs)) < 0) {
1042 RWDEBUG("Attribute name refers to outer request but not in a tunnel (skipping)");
1043 continue;
1044 }
1045
1046 if (!tmpl_list_head(current, tmpl_list(map->lhs))) {
1047 RWDEBUG("List not valid in this context (skipping)");
1048 continue;
1049 }
1050
1051 /*
1052 * Alternative JSON structure which allows operator,
1053 * and other flags to be specified.
1054 *
1055 * "<name>":{
1056 * "do_xlat":<bool>,
1057 * "is_json":<bool>,
1058 * "op":"<op>",
1059 * "value":<value>
1060 * }
1061 *
1062 * Where value is a:
1063 * - [] Multivalued array
1064 * - {} Nested Valuepair
1065 * - * Integer or string value
1066 */
1067 if (json_object_is_type(value, json_type_object)) {
1068 /*
1069 * Process operator if present.
1070 */
1071 if (json_object_object_get_ex(value, "op", &tmp)) {
1072 map->op = fr_table_value_by_str(fr_tokens_table, json_object_get_string(tmp), 0);
1073 if (!map->op) {
1074 RWDEBUG("Invalid operator value \"%s\" (skipping)",
1075 json_object_get_string(tmp));
1076 continue;
1077 }
1078 }
1079
1080 /*
1081 * Process optional do_xlat bool.
1082 */
1083 if (json_object_object_get_ex(value, "do_xlat", &tmp)) {
1084 value_ctx.do_xlat = json_object_get_boolean(tmp);
1085 }
1086
1087 /*
1088 * Process optional is_json bool.
1089 */
1090 if (json_object_object_get_ex(value, "is_json", &tmp)) {
1091 value_ctx.is_json = json_object_get_boolean(tmp);
1092 }
1093
1094 /*
1095 * Value key must be present if were using the expanded syntax.
1096 */
1097 if (!json_object_object_get_ex(value, "value", &tmp)) {
1098 RWDEBUG("Value key missing (skipping)");
1099 continue;
1100 }
1101
1102 /*
1103 * The value field now becomes the key we're operating on
1104 */
1105 value = tmp;
1106 }
1107
1108 /*
1109 * Setup fr_pair_afrom_da / recursion loop.
1110 */
1111 if (!value_ctx.is_json && json_object_is_type(value, json_type_array)) {
1112 elements = json_object_array_length(value);
1113 if (!elements) {
1114 RWDEBUG("Zero length value array (skipping)");
1115 continue;
1116 }
1117 element = json_object_array_get_idx(value, 0);
1118 } else {
1119 elements = 1;
1120 element = value;
1121 }
1122
1123 /*
1124 * A JSON 'value' key, may have multiple elements, iterate
1125 * over each of them, creating a new fr_pair_t.
1126 */
1127 do {
1128 if (max_attrs-- <= 0) {
1129 RWDEBUG("At maximum attribute limit");
1130 talloc_free(map);
1131 return max;
1132 }
1133
1134 /*
1135 * Automagically switch the op for multivalued attributes.
1136 */
1137 if (((map->op == T_OP_SET) || (map->op == T_OP_EQ)) && (i >= 1)) {
1138 map->op = T_OP_ADD_EQ;
1139 }
1140
1141 if (json_object_is_type(element, json_type_object) && !value_ctx.is_json) {
1142 /* TODO: Insert nested VP into VP structure...*/
1143 RWDEBUG("Found nested VP, these are not yet supported (skipping)");
1144
1145 continue;
1146
1147 /*
1148 vp = json_pair_alloc(instance, section,
1149 request, value,
1150 level + 1, max_attrs);*/
1151 } else {
1152 if (tmpl_afrom_substr(map, &map->rhs,
1153 &FR_SBUFF_IN(json_object_get_string(element),
1154 (size_t) json_object_get_string_len(element)),
1155 T_SINGLE_QUOTED_STRING, NULL, NULL) < 0) {
1156 REDEBUG("Failed parsing value into map");
1157 continue;
1158 }
1159 value_ctx.value = element;
1160 map_to_request(request, map, json_map_getvalue, &value_ctx);
1161 TALLOC_FREE(map->rhs);
1162 }
1163 /*
1164 * If we call json_object_array_get_idx on something that's not an array
1165 * the behaviour appears to be to occasionally segfault.
1166 */
1167 } while ((++i < elements) && (element = json_object_array_get_idx(value, i)));
1168 }
1169 }
1170
1171 talloc_free(map);
1172
1173 return max - max_attrs;
1174}
1175
1176/** Converts JSON response into fr_pair_ts and adds them to the request.
1177 *
1178 * Converts the raw JSON string into a json-c object tree and passes it to
1179 * json_pair_alloc. After the tree has been parsed json_object_put is called
1180 * which decrements the reference count of the root node by one, and frees
1181 * the entire tree.
1182 *
1183 * @see rest_encode_json
1184 * @see json_pair_alloc
1185 *
1186 * @param[in] instance configuration data.
1187 * @param[in] section configuration data.
1188 * @param[in,out] request Current request.
1189 * @param[in] randle REST handle.
1190 * @param[in] raw buffer containing JSON data.
1191 * @param[in] rawlen Length of data in raw buffer.
1192 * @return
1193 * - The number of #fr_pair_t processed.
1194 * - -1 on unrecoverable error.
1195 */
1196static int rest_decode_json(rlm_rest_t const *instance, rlm_rest_section_t const *section,
1197 request_t *request, UNUSED fr_curl_io_request_t *randle, char *raw, UNUSED size_t rawlen)
1198{
1199 char const *p = raw;
1200
1201 struct json_object *json;
1202
1203 int ret;
1204
1205 /*
1206 * Empty response?
1207 */
1209 if (*p == '\0') return 0;
1210
1211 json = json_tokener_parse(p);
1212 if (!json) {
1213 REDEBUG("Malformed JSON data \"%s\"", raw);
1214 return -1;
1215 }
1216
1217 ret = json_pair_alloc(instance, section, request, json, 0, REST_BODY_MAX_ATTRS);
1218
1219 /*
1220 * Decrement reference count for root object, should free entire JSON tree.
1221 */
1222 json_object_put(json);
1223
1224 return ret;
1225}
1226#endif
1227
1228/** Processes incoming HTTP header data from libcurl.
1229 *
1230 * Processes the status line, and Content-Type headers from the incoming HTTP
1231 * response.
1232 *
1233 * Matches prototype for CURLOPT_HEADERFUNCTION, and will be called directly
1234 * by libcurl.
1235 *
1236 * @param[in] in Char buffer where inbound header data is written.
1237 * @param[in] size Multiply by nmemb to get the length of ptr.
1238 * @param[in] nmemb Multiply by size to get the length of ptr.
1239 * @param[in] userdata rlm_rest_response_t to keep parsing state between calls.
1240 * @return
1241 * - Length of data processed.
1242 * - 0 on error.
1243 */
1244static size_t rest_response_header(void *in, size_t size, size_t nmemb, void *userdata)
1245{
1246 rlm_rest_response_t *ctx = userdata;
1247 request_t *request = ctx->request; /* Used by RDEBUG */
1248
1249 char const *start = (char *)in, *p = start, *end = p + (size * nmemb);
1250 char const *q;
1251 size_t len;
1252
1254
1255#ifndef NDEBUG
1256 if (ctx->instance->fail_header_decode) {
1257 REDEBUG("Forcing header decode failure");
1258 return 0;
1259 }
1260#endif
1261
1262 /*
1263 * This seems to be curl's indication there are no more header lines.
1264 */
1265 if (((end - p) == 2) && ((p[0] == '\r') && (p[1] == '\n'))) {
1266 /*
1267 * If we got a 100 Continue, we need to send additional payload data.
1268 * reset the state to WRITE_STATE_INIT, so that when were called again
1269 * we overwrite previous header data with that from the proper header.
1270 */
1271 if (ctx->code == 100) {
1272 RDEBUG2("Continuing...");
1273 ctx->state = WRITE_STATE_INIT;
1274 }
1275
1276 return (end - start);
1277 }
1278
1279 switch (ctx->state) {
1280 case WRITE_STATE_INIT:
1281 RDEBUG2("Processing response header");
1282
1283 /*
1284 * HTTP/<version> <reason_code>[ <reason_phrase>]\r\n
1285 *
1286 * "HTTP/1.1 " (9) + "100" (3) + "\r\n" (2) = 14
1287 * "HTTP/2 " (7) + "100" (3) + "\r\n" (2) = 12
1288 */
1289 if ((end - p) < 12) {
1290 REDEBUG("Malformed HTTP header: Status line too short");
1291 malformed:
1292 REDEBUG("Received %td bytes of invalid header data: %pV",
1293 (end - start), fr_box_strvalue_len(in, (end - start)));
1294 ctx->code = 0;
1295
1296 /*
1297 * Indicate we parsed the entire line, otherwise
1298 * bad things seem to happen internally with
1299 * libcurl when we try and use it with asynchronous
1300 * I/O handlers.
1301 */
1302 return (end - start);
1303 }
1304 /*
1305 * Check start of header matches...
1306 */
1307 if (strncasecmp("HTTP/", p, 5) != 0) {
1308 REDEBUG("Malformed HTTP header: Missing HTTP version");
1309 goto malformed;
1310 }
1311 p += 5;
1312
1313 /*
1314 * Skip the version field, next space should mark start of reason_code.
1315 */
1316 q = memchr(p, ' ', (end - p));
1317 if (!q) {
1318 REDEBUG("Malformed HTTP header: Missing reason code");
1319 goto malformed;
1320 }
1321
1322 p = q;
1323
1324 /*
1325 * Process reason_code.
1326 *
1327 * " 100" (4) + "\r\n" (2) = 6
1328 */
1329 if ((end - p) < 6) {
1330 REDEBUG("Malformed HTTP header: Reason code too short");
1331 goto malformed;
1332 }
1333 p++;
1334
1335 /*
1336 * "xxx( |\r)" status code and terminator.
1337 */
1338 if (!isdigit(p[0]) || !isdigit(p[1]) || !isdigit(p[2]) || !((p[3] == ' ') || (p[3] == '\r'))) {
1339 REDEBUG("Malformed HTTP header: Reason code malformed. "
1340 "Expected three digits then space or end of header, got \"%pV\"",
1341 fr_box_strvalue_len(p, 4));
1342 goto malformed;
1343 }
1344
1345 /*
1346 * Convert status code into an integer value. strtoul needs
1347 * a writable endptr, so shadow q in a local scope.
1348 */
1349 {
1350 char *qq = NULL;
1351
1352 ctx->code = (int)strtoul(p, &qq, 10);
1353 fr_assert(qq == (p + 3)); /* We check this above */
1354 p = qq;
1355 }
1356
1357 /*
1358 * Process reason_phrase (if present).
1359 */
1360 RINDENT();
1361 if (*p == ' ') {
1362 p++;
1363 q = memchr(p, '\r', (end - p));
1364 if (!q) goto malformed;
1365 RDEBUG2("Status : %i (%pV)", ctx->code, fr_box_strvalue_len(p, q - p));
1366 } else {
1367 RDEBUG2("Status : %i", ctx->code);
1368 }
1369 REXDENT();
1370
1372
1373 break;
1374
1376 if (((end - p) >= 14) &&
1377 (strncasecmp("Content-Type: ", p, 14) == 0)) {
1378 p += 14;
1379
1380 /*
1381 * Check to see if there's a parameter separator.
1382 */
1383 q = memchr(p, ';', (end - p));
1384
1385 /*
1386 * If there's not, find the end of this header.
1387 */
1388 if (!q) q = memchr(p, '\r', (end - p));
1389
1390 len = (size_t)(!q ? (end - p) : (q - p));
1392
1393 RINDENT();
1394 RDEBUG2("Type : %s (%pV)", fr_table_str_by_value(http_body_type_table, type, "<INVALID>"),
1396 REXDENT();
1397
1398 /*
1399 * Assume the force_to value has already been validated.
1400 */
1401 if (ctx->force_to != REST_HTTP_BODY_UNKNOWN) {
1402 if (ctx->force_to != ctx->type) {
1403 RDEBUG3("Forcing body type to \"%s\"",
1405 ctx->type = ctx->force_to;
1406 }
1407 /*
1408 * Figure out if the type is supported by one of the decoders.
1409 */
1410 } else {
1411 /*
1412 * No need to check supported types if we accept everything.
1413 */
1414 if (ctx->section->response.accept_all) {
1415 ctx->type = type;
1416 break;
1417 }
1418
1420
1421 switch (ctx->type) {
1423 RWDEBUG("Couldn't determine type, using the request's type \"%s\".",
1425 break;
1426
1428 REDEBUG("Type \"%s\" is currently unsupported",
1430 break;
1431
1433 REDEBUG("Type \"%s\" is unavailable, please rebuild this module with the required "
1434 "library", fr_table_str_by_value(http_body_type_table, type, "<INVALID>"));
1435 break;
1436
1438 REDEBUG("Type \"%s\" is not a valid web API data markup format",
1440 break;
1441
1442 /* supported type */
1443 default:
1444 break;
1445 }
1446 }
1447 }
1448 break;
1449
1450 default:
1451 break;
1452 }
1453
1454 return (end - start);
1455}
1456
1457/** Processes incoming HTTP body data from libcurl.
1458 *
1459 * Writes incoming body data to an intermediary buffer for later parsing by
1460 * one of the decode functions.
1461 *
1462 * @param[in] in Char buffer where inbound header data is written
1463 * @param[in] size Multiply by nmemb to get the length of ptr.
1464 * @param[in] nmemb Multiply by size to get the length of ptr.
1465 * @param[in] userdata rlm_rest_response_t to keep parsing state between calls.
1466 * @return
1467 * - Length of data processed.
1468 * - 0 on error.
1469 */
1470static size_t rest_response_body(void *in, size_t size, size_t nmemb, void *userdata)
1471{
1472 rlm_rest_response_t *ctx = userdata;
1473 request_t *request = ctx->request; /* Used by RDEBUG */
1474
1475 char const *start = in, *p = start, *end = p + (size * nmemb);
1476 char const *q;
1477
1478 size_t needed;
1479
1480 if (start == end) return 0; /* Nothing to process */
1481
1482#ifndef NDEBUG
1483 if (ctx->instance->fail_body_decode) {
1484 REDEBUG("Forcing body read failure");
1485 return 0;
1486 }
1487#endif
1488
1489 /*
1490 * Any post processing of headers should go here...
1491 */
1493
1494 switch (ctx->type) {
1498 if (ctx->section->response.accept_all) goto accept_body;
1499
1500 while ((q = memchr(p, '\n', (end - p)))) {
1501 REDEBUG("%pV", fr_box_strvalue_len(p, q - p));
1502 p = q + 1;
1503 }
1504
1505 if (p != end) REDEBUG("%pV", fr_box_strvalue_len(p, end - p));
1506 break;
1507
1509 while ((q = memchr(p, '\n', (end - p)))) {
1510 RDEBUG3("%pV", fr_box_strvalue_len(p, q - p));
1511 p = q + 1;
1512 }
1513
1514 if (p != end) RDEBUG3("%pV", fr_box_strvalue_len(p, end - p));
1515 break;
1516
1517 default:
1518 {
1519 char *out_p;
1520 accept_body:
1521 if ((ctx->section->response.max_body_in > 0) && ((ctx->used + (end - p)) > ctx->section->response.max_body_in)) {
1522 REDEBUG("Incoming data (%zu bytes) exceeds max_body_in (%zu bytes). "
1523 "Forcing body to type 'invalid'", ctx->used + (end - p), ctx->section->response.max_body_in);
1525 TALLOC_FREE(ctx->buffer);
1526 break;
1527 }
1528
1529 needed = ROUND_UP(ctx->used + (end - p), REST_BODY_ALLOC_CHUNK);
1530 if (needed > ctx->alloc) {
1531 MEM(ctx->buffer = talloc_bstr_realloc(NULL, ctx->buffer, needed));
1532 ctx->alloc = needed;
1533 }
1534
1535 out_p = ctx->buffer + ctx->used;
1536 memcpy(out_p, p, (end - p));
1537 out_p += (end - p);
1538 *out_p = '\0';
1539 ctx->used += (end - p);
1540 }
1541 break;
1542 }
1543
1544 return (end - start);
1545}
1546
1547/** Print out the response text as error lines
1548 *
1549 * @param request The Current request.
1550 * @param handle fr_curl_io_request_t used to execute the previous request.
1551 */
1553{
1554 char const *p, *end;
1555 char const *q;
1556 size_t len;
1557
1558 len = rest_get_handle_data(&p, handle);
1559 if (len == 0) return;
1560
1561 end = p + len;
1562
1563 RERROR("Server returned:");
1564 while ((q = memchr(p, '\n', (end - p)))) {
1565 RERROR("%pV", fr_box_strvalue_len(p, q - p));
1566 p = q + 1;
1567 }
1568
1569 if (p != end) RERROR("%pV", fr_box_strvalue_len(p, end - p));
1570}
1571
1572/** Print out the response text
1573 *
1574 * @param request The Current request.
1575 * @param handle fr_curl_io_request_t used to execute the previous request.
1576 */
1578{
1579 char const *p, *end;
1580 char const *q;
1581 size_t len;
1582
1583 len = rest_get_handle_data(&p, handle);
1584 if (len == 0) return;
1585
1586 end = p + len;
1587
1588 RDEBUG3("Server returned:");
1589 while ((q = memchr(p, '\n', (end - p)))) {
1590 RDEBUG3("%pV", fr_box_strvalue_len(p, q - p));
1591 p = q + 1;
1592 }
1593
1594 if (p != end) RDEBUG3("%pV", fr_box_strvalue_len(p, end - p));
1595}
1596
1597/** (Re-)Initialises the data in a rlm_rest_response_t.
1598 *
1599 * This resets the values of the a rlm_rest_response_t to their defaults.
1600 * Must be called between encoding sessions.
1601 *
1602 * @see rest_response_body
1603 * @see rest_response_header
1604 *
1605 * @param[in] section that created the request.
1606 * @param[in] request Current request.
1607 * @param[in] ctx data to initialise.
1608 * @param[in] type Default http_body_type to use when decoding raw data, may be
1609 * overwritten by rest_response_header.
1610 * @param[in] header Where to write out headers, may be NULL.
1611 */
1612static void rest_response_init(rlm_rest_section_t const *section,
1614{
1615 ctx->section = section;
1616 ctx->request = request;
1617 ctx->type = type;
1618 ctx->state = WRITE_STATE_INIT;
1619 ctx->alloc = 0;
1620 ctx->used = 0;
1621 ctx->code = 0;
1622 ctx->header = header;
1623 TALLOC_FREE(ctx->buffer);
1624}
1625
1626/** Extracts pointer to buffer containing response data
1627 *
1628 * @param[out] out Where to write the pointer to the buffer.
1629 * @param[in] randle used for the last request.
1630 * @return
1631 * - 0 if no data i available.
1632 * - > 0 if data is available.
1633 */
1634size_t rest_get_handle_data(char const **out, fr_curl_io_request_t *randle)
1635{
1636 rlm_rest_curl_context_t *ctx = talloc_get_type_abort(randle->uctx, rlm_rest_curl_context_t);
1637
1638 if (!ctx->response.buffer) return 0;
1639
1640 *out = ctx->response.buffer;
1641 return ctx->response.used;
1642}
1643
1644/** Return the body type of a HTTP response
1645 *
1646 * @param[in] randle used for the last request.
1647 * @return
1648 * - http_body_type_t
1649 */
1651{
1652 rlm_rest_curl_context_t *ctx = talloc_get_type_abort(randle->uctx, rlm_rest_curl_context_t);
1653
1654 return ctx->response.type;
1655}
1656
1657/** Configures body specific curlopts.
1658 *
1659 * Configures libcurl handle to use either chunked mode, where the request
1660 * data will be sent using multiple HTTP requests, or contiguous mode where
1661 * the request data will be sent in a single HTTP request.
1662 *
1663 * @param[in] mctx Call data.
1664 * @param[in] section configuration data.
1665 * @param[in] request Current request.
1666 * @param[in] randle fr_curl_io_request_t to configure.
1667 * @param[in] func to pass to libcurl for chunked.
1668 * transfers (NULL if not using chunked mode).
1669 * @return
1670 * - 0 on success.
1671 * - -1 on failure.
1672 */
1673static int rest_request_config_body(module_ctx_t const *mctx, rlm_rest_section_t const *section,
1675{
1676 rlm_rest_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_rest_t);
1677 rlm_rest_curl_context_t *uctx = talloc_get_type_abort(randle->uctx, rlm_rest_curl_context_t);
1678 ssize_t len;
1679
1680 /*
1681 * We were provided with no read function, assume this means
1682 * no body should be sent.
1683 */
1684 if (!func) {
1685 FR_CURL_REQUEST_SET_OPTION(CURLOPT_POSTFIELDSIZE, 0L);
1686 return 0;
1687 }
1688
1689 /*
1690 * Chunked transfer encoding means the body will be sent in
1691 * multiple parts.
1692 */
1693 if (section->request.chunk > 0) {
1694 FR_CURL_REQUEST_SET_OPTION(CURLOPT_READDATA, &uctx->request);
1695 FR_CURL_REQUEST_SET_OPTION(CURLOPT_READFUNCTION, func);
1696
1697 return 0;
1698 }
1699
1700 /*
1701 * If were not doing chunked encoding then we read the entire
1702 * body into a buffer, and send it in one go.
1703 */
1705 if (len <= 0) {
1706 REDEBUG("Failed creating HTTP body content");
1707 return -1;
1708 }
1709 RDEBUG2("Content-Length will be %zu bytes", (size_t)len);
1710
1711 fr_assert((len == 0) || (talloc_array_length(uctx->body) >= (size_t)len));
1712 FR_CURL_REQUEST_SET_OPTION(CURLOPT_POSTFIELDS, uctx->body);
1713 FR_CURL_REQUEST_SET_OPTION(CURLOPT_POSTFIELDSIZE, len);
1714
1715 return 0;
1716
1717error:
1718 return -1;
1719}
1720
1721/** Adds an additional header to a handle to use in the next reques
1722 *
1723 * @param[in] request Current request.
1724 * @param[in] randle used for the next request.
1725 * @param[in] header to add.
1726 * @param[in] validate whether to perform basic checks on the header
1727 * @return
1728 * - 0 on success.
1729 * - -1 on failure.
1730 */
1731int rest_request_config_add_header(request_t *request, fr_curl_io_request_t *randle, char const *header, bool validate)
1732{
1733 rlm_rest_curl_context_t *ctx = talloc_get_type_abort(randle->uctx, rlm_rest_curl_context_t);
1734 struct curl_slist *headers;
1735
1736 if (validate && !strchr(header, ':')) {
1737 RWDEBUG("Invalid HTTP header \"%s\" must be in format '<attribute>: <value>'. Skipping...",
1738 header);
1739 return -1;
1740 }
1741
1742 RINDENT();
1743 RDEBUG3("%s", header);
1744 REXDENT();
1745
1746 headers = curl_slist_append(ctx->headers, header);
1747 if (unlikely(!headers)) {
1748 REDEBUG("Failed to add header \"%s\"", header);
1749 return -1;
1750 }
1751 ctx->headers = headers;
1752
1753 return 0;
1754}
1755
1756/** See if the list of headers already contains a header
1757 *
1758 * @note Only compares the header name, not the value.
1759 *
1760 * @param[in] randle to check headers for.
1761 * @param[in] header to find.
1762 * @return
1763 * - true if yes
1764 * - false if no
1765 */
1766static bool rest_request_contains_header(fr_curl_io_request_t *randle, char const *header)
1767{
1768 rlm_rest_curl_context_t *ctx = talloc_get_type_abort(randle->uctx, rlm_rest_curl_context_t);
1769 struct curl_slist *headers = ctx->headers;
1770 char const *sep;
1771 size_t cmp_len;
1772
1773 sep = strchr(header, ':');
1774 cmp_len = sep ? (size_t)(sep - header) : strlen(header);
1775
1776 while (headers) {
1777 if (strncasecmp(headers->data, header, cmp_len) == 0) return true;
1778 headers = headers->next;
1779 }
1780
1781 return false;
1782}
1783
1784/** Configures request curlopts.
1785 *
1786 * Configures libcurl handle setting various curlopts for things like local
1787 * client time, Content-Type, and other FreeRADIUS custom headers.
1788 *
1789 * Current FreeRADIUS custom headers are:
1790 * - X-FreeRADIUS-Section The module section being processed.
1791 * - X-FreeRADIUS-Server The current virtual server the request_t is
1792 * passing through.
1793 *
1794 * Sets up callbacks for all response processing (buffers and body data).
1795 *
1796 * @param[in] mctx call data.
1797 * @param[in] section configuration data.
1798 * @param[in] randle to configure.
1799 * @param[in] request Current request.
1800 * @param[in] method to use (HTTP verbs PUT, POST, DELETE etc...).
1801 * @param[in] type Content-Type for request encoding, also sets
1802 * the default for decoding.
1803 * @param[in] uri buffer containing the expanded URI to send the request to.
1804 * @param[in] body_data (optional) custom body data. Must persist whilst we're
1805 * writing data out to the socket. Must be a talloced buffer
1806 * which is \0 terminated.
1807 * @return
1808 * - 0 on success (all opts configured).
1809 * - -1 on failure.
1810 */
1814 char const *uri, char const *body_data)
1815{
1816 rlm_rest_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_rest_t);
1817 rlm_rest_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
1818 rlm_rest_curl_context_t *ctx = talloc_get_type_abort(randle->uctx, rlm_rest_curl_context_t);
1819 CURL *candle = randle->candle;
1820 fr_time_delta_t timeout;
1821
1822 http_auth_type_t auth = section->request.auth;
1823
1824 CURLcode ret = CURLE_OK;
1825 char const *option;
1826
1827 char buffer[512];
1828
1829 fr_assert(candle);
1830
1831 buffer[(sizeof(buffer) - 1)] = '\0';
1832
1833 /*
1834 * Control which HTTP version we're going to use
1835 */
1836 if (inst->http_negotiation != CURL_HTTP_VERSION_NONE) FR_CURL_REQUEST_SET_OPTION(CURLOPT_HTTP_VERSION, (long)inst->http_negotiation);
1837
1838 /*
1839 * Setup any header options and generic headers.
1840 */
1841 FR_CURL_REQUEST_SET_OPTION(CURLOPT_URL, uri);
1842#if CURL_AT_LEAST_VERSION(7,85,0)
1843 FR_CURL_REQUEST_SET_OPTION(CURLOPT_PROTOCOLS_STR, "http,https");
1844#else
1845 FR_CURL_REQUEST_SET_OPTION(CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
1846#endif
1847 if (section->request.proxy) {
1848 if (section->request.proxy == rest_no_proxy) {
1849 FR_CURL_REQUEST_SET_OPTION(CURLOPT_NOPROXY, "*");
1850 } else {
1851 FR_CURL_REQUEST_SET_OPTION(CURLOPT_PROXY, section->request.proxy);
1852 }
1853 }
1854 FR_CURL_REQUEST_SET_OPTION(CURLOPT_NOSIGNAL, 1L);
1855 FR_CURL_REQUEST_SET_OPTION(CURLOPT_USERAGENT, "FreeRADIUS " RADIUSD_VERSION_STRING);
1856
1857 timeout = inst->conn_config.connect_timeout;
1858 RDEBUG3("Connect timeout is %pVs, request timeout is %pVs",
1859 fr_box_time_delta(timeout), fr_box_time_delta(section->timeout));
1860 FR_CURL_REQUEST_SET_OPTION(CURLOPT_CONNECTTIMEOUT_MS, fr_time_delta_to_msec(timeout));
1861 FR_CURL_REQUEST_SET_OPTION(CURLOPT_TIMEOUT_MS, fr_time_delta_to_msec(section->timeout));
1862
1863 /*
1864 * FreeRADIUS custom headers
1865 */
1866 RDEBUG3("Adding custom headers:");
1867 snprintf(buffer, sizeof(buffer), "X-FreeRADIUS-Section: %s", section->name);
1868 if (unlikely(rest_request_config_add_header(request, randle, buffer, false) < 0)) return -1;
1869
1870 snprintf(buffer, sizeof(buffer), "X-FreeRADIUS-Server: %s", unlang_interpret_virtual_server(request));
1871 if (unlikely(rest_request_config_add_header(request, randle, buffer, false) < 0)) return -1;
1872
1873 /*
1874 * Add in the section headers
1875 */
1876 if (call_env->request.header) {
1877 size_t len = talloc_array_length(call_env->request.header), i;
1878 for (i = 0; i < len; i++) {
1879 fr_value_box_list_foreach(&call_env->request.header[i], header) {
1880 if (unlikely(rest_request_config_add_header(request, randle, header->vb_strvalue, true) < 0)) return -1;
1881 }
1882 }
1883 }
1884
1885 /*
1886 * Add in dynamic headers from the request
1887 */
1888 {
1889 fr_pair_t *header;
1890 fr_dcursor_t header_cursor;
1891
1892 for (header = fr_pair_dcursor_by_da_init(&header_cursor, &request->control_pairs, attr_rest_http_header);
1893 header;
1894 header = fr_dcursor_current(&header_cursor)) {
1895 header = fr_dcursor_remove(&header_cursor);
1896 if (unlikely(rest_request_config_add_header(request, randle, header->vp_strvalue, true) < 0)) return -1;
1897
1898 talloc_free(header);
1899 }
1900 }
1901
1902 if (!rest_request_contains_header(randle, "Content-Type:")) {
1903 /*
1904 * HTTP/1.1 doesn't require a content type so only set it
1905 * if where body type requires it, and we haven't set one
1906 * already from attributes.
1907 */
1908 if (type != REST_HTTP_BODY_NONE) {
1909 char const *content_type = fr_table_str_by_value(http_content_type_table, type, section->request.body_str);
1910 snprintf(buffer, sizeof(buffer), "Content-Type: %s", content_type);
1911 if (unlikely(rest_request_config_add_header(request, randle, buffer, false) < 0)) return -1;
1912
1913 RDEBUG3("Request body content-type will be \"%s\"", content_type);
1914 }
1915 }
1916
1917 /*
1918 * Configure HTTP verb (GET, POST, PUT, PATCH, DELETE, other...)
1919 */
1920 switch (method) {
1922 FR_CURL_REQUEST_SET_OPTION(CURLOPT_HTTPGET, 1L);
1923 break;
1924
1926 FR_CURL_REQUEST_SET_OPTION(CURLOPT_POST, 1L);
1927 break;
1928
1930 /*
1931 * Do not set CURLOPT_PUT, this will cause libcurl
1932 * to ignore CURLOPT_POSTFIELDs and attempt to read
1933 * whatever was set with CURLOPT_READDATA, which by
1934 * default is stdin.
1935 *
1936 * This is many cases will cause the server to block,
1937 * indefinitely.
1938 */
1939 FR_CURL_REQUEST_SET_OPTION(CURLOPT_CUSTOMREQUEST, "PUT");
1940 break;
1941
1943 FR_CURL_REQUEST_SET_OPTION(CURLOPT_CUSTOMREQUEST, "PATCH");
1944 break;
1945
1947 FR_CURL_REQUEST_SET_OPTION(CURLOPT_CUSTOMREQUEST, "DELETE");
1948 break;
1949
1951 FR_CURL_REQUEST_SET_OPTION(CURLOPT_CUSTOMREQUEST, section->request.method_str);
1952 break;
1953
1954 default:
1955 fr_assert(0);
1956 break;
1957 }
1958
1959 /*
1960 * Set user based authentication parameters
1961 */
1962 if (auth > REST_HTTP_AUTH_NONE) {
1963
1964#define SET_AUTH_OPTION(_x, _y)\
1965do {\
1966 if ((ret = curl_easy_setopt(candle, _x, _y)) != CURLE_OK) {\
1967 option = STRINGIFY(_x);\
1968 REDEBUG("Failed setting curl option %s: %s (%u)", option, curl_easy_strerror(ret), ret); \
1969 goto error;\
1970 }\
1971} while (0)
1972 RDEBUG3("Configuring HTTP auth type %s, user \"%pV\", password \"%pV\"",
1973 fr_table_str_by_value(http_auth_table, auth, "<INVALID>"),
1974 call_env->request.username ? call_env->request.username : fr_box_strvalue("(none)"),
1975 call_env->request.password ? call_env->request.password : fr_box_strvalue("(none)"));
1976
1977 /*
1978 * FIXME - We probably want to escape \0s here...
1979 */
1980 if ((auth >= REST_HTTP_AUTH_BASIC) && (auth <= REST_HTTP_AUTH_ANY_SAFE)) {
1981 SET_AUTH_OPTION(CURLOPT_HTTPAUTH, http_curl_auth[auth]);
1982 if (call_env->request.username) SET_AUTH_OPTION(CURLOPT_USERNAME, call_env->request.username->vb_strvalue);
1983 if (call_env->request.password) SET_AUTH_OPTION(CURLOPT_PASSWORD, call_env->request.password->vb_strvalue);
1984 } else if (auth == REST_HTTP_AUTH_TLS_SRP) {
1985 SET_AUTH_OPTION(CURLOPT_TLSAUTH_TYPE, "SRP");
1986 if (call_env->request.username) SET_AUTH_OPTION(CURLOPT_TLSAUTH_USERNAME, call_env->request.username->vb_strvalue);
1987 if (call_env->request.password) SET_AUTH_OPTION(CURLOPT_TLSAUTH_PASSWORD, call_env->request.password->vb_strvalue);
1988 }
1989 }
1990
1991 /*
1992 * Set SSL/TLS authentication parameters
1993 */
1994 fr_curl_easy_tls_init(randle, &section->tls);
1995
1996 /*
1997 * Tell CURL how to get HTTP body content, and how to process incoming data.
1998 */
1999 rest_response_init(section, request, &ctx->response, type, call_env->response.header);
2000
2001 FR_CURL_REQUEST_SET_OPTION(CURLOPT_HEADERFUNCTION, rest_response_header);
2002 FR_CURL_REQUEST_SET_OPTION(CURLOPT_HEADERDATA, &ctx->response);
2003 FR_CURL_REQUEST_SET_OPTION(CURLOPT_WRITEFUNCTION, rest_response_body);
2004 FR_CURL_REQUEST_SET_OPTION(CURLOPT_WRITEDATA, &ctx->response);
2005
2006 /*
2007 * Force parsing the body text as a particular encoding.
2008 */
2009 ctx->response.force_to = section->response.force_to;
2010
2011 switch (method) {
2014 RDEBUG3("Using a HTTP method which does not require a body. Forcing request body type to \"none\"");
2015 goto finish;
2016
2021 if (section->request.chunk > 0) {
2022 ctx->request.chunk = section->request.chunk;
2023
2024 if (unlikely(rest_request_config_add_header(request, randle, "Transfer-Encoding: chunked", false) < 0)) return -1;
2025 }
2026
2027 break;
2028
2029 default:
2030 fr_assert(0);
2031 }
2032
2033 /*
2034 * Setup encoder specific options
2035 */
2036 switch (type) {
2038 if (rest_request_config_body(mctx, section, request, randle, NULL) < 0) return -1;
2039
2040 break;
2041
2043 {
2045
2046 data = talloc_zero(request, rest_custom_data_t);
2047 data->p = data->start = body_data;
2048 data->len = talloc_strlen(body_data);
2049
2050 /* Use the encoder specific pointer to store the data we need to encode */
2051 ctx->request.encoder = data;
2052 if (rest_request_config_body(mctx, section, request, randle, rest_encode_custom) < 0) {
2053 TALLOC_FREE(ctx->request.encoder);
2054 return -1;
2055 }
2056 }
2057 break;
2058
2059#ifdef HAVE_JSON
2061 {
2063
2064 data = talloc_zero(request, rest_custom_data_t);
2065 ctx->request.encoder = data;
2066
2067 rest_request_init(section, request, &ctx->request);
2068
2069 if (rest_request_config_body(mctx, section, request, randle, rest_encode_json) < 0) return -1;
2070 }
2071
2072 break;
2073#endif
2074
2076 rest_request_init(section, request, &ctx->request);
2077 fr_pair_dcursor_init(&(ctx->request.cursor), &request->request_pairs);
2078
2079 if (rest_request_config_body(mctx, section, request, randle, rest_encode_post) < 0) return -1;
2080
2081 break;
2082
2083 default:
2084 fr_assert(0);
2085 }
2086
2087
2088finish:
2089 FR_CURL_REQUEST_SET_OPTION(CURLOPT_HTTPHEADER, ctx->headers);
2090
2091 return 0;
2092
2093error:
2094 return -1;
2095}
2096
2097/** Sends the response to the correct decode function.
2098 *
2099 * Uses the Content-Type information written in rest_response_header to
2100 * determine the correct decode function to use. The decode function will
2101 * then convert the raw received data into fr_pair_ts.
2102 *
2103 * @param[in] instance configuration data.
2104 * @param[in] section configuration data.
2105 * @param[in] request Current request.
2106 * @param[in] randle to use.
2107 * @return
2108 * - 0 on success.
2109 * - -1 on failure.
2110 */
2111int rest_response_decode(rlm_rest_t const *instance, rlm_rest_section_t const *section,
2113{
2114 rlm_rest_curl_context_t *ctx = talloc_get_type_abort(randle->uctx, rlm_rest_curl_context_t);
2115
2116 int ret = -1; /* -Wsometimes-uninitialized */
2117
2118 if (!ctx->response.buffer) {
2119 RDEBUG2("Skipping attribute processing, no valid body data received");
2120 return 0;
2121 }
2122
2123 switch (ctx->response.type) {
2125 return 0;
2126
2128 ret = rest_decode_plain(instance, section, request, randle, ctx->response.buffer, ctx->response.used);
2129 break;
2130
2132 ret = rest_decode_post(instance, section, request, randle, ctx->response.buffer, ctx->response.used);
2133 break;
2134
2135#ifdef HAVE_JSON
2137 ret = rest_decode_json(instance, section, request, randle, ctx->response.buffer, ctx->response.used);
2138 break;
2139#endif
2140
2144 return -1;
2145
2146 default:
2147 fr_assert(0);
2148 }
2149
2150 return ret;
2151}
2152
2153/** URL encodes a string.
2154 *
2155 * Encode special chars as per RFC 3986 section 4.
2156 *
2157 * @param[in] request Current request.
2158 * @param[out] out Where to write escaped string.
2159 * @param[in] outlen Size of out buffer.
2160 * @param[in] raw string to be urlencoded.
2161 * @param[in] arg pointer, gives context for escaping.
2162 * @return length of data written to out (excluding NULL).
2163 */
2164size_t rest_uri_escape(UNUSED request_t *request, char *out, size_t outlen, char const *raw, UNUSED void *arg)
2165{
2166 char *escaped;
2167
2168 escaped = curl_escape(raw, 0);
2169 strlcpy(out, escaped, outlen);
2170 curl_free(escaped);
2171
2172 return strlen(out);
2173}
2174
2175/** Unescapes the host portion of a URI string
2176 *
2177 * This is required because the xlat functions which operate on the input string
2178 * cannot distinguish between host and path components.
2179 *
2180 * @param[out] out Where to write the pointer to the new
2181 * buffer containing the escaped URI.
2182 * @param[in] inst of rlm_rest.
2183 * @param[in] request Current request
2184 * @param[in] randle to use.
2185 * @param[in] uri configuration data.
2186 * @return
2187 * - Length of data written to buffer (excluding NULL).
2188 * - < 0 if an error occurred.
2189 */
2191 fr_curl_io_request_t *randle, char const *uri)
2192{
2193 CURL *candle = randle->candle;
2194
2195 char const *p, *q;
2196
2197 char *scheme;
2198
2199 ssize_t len;
2200
2201 p = uri;
2202
2203 /*
2204 * All URLs must contain at least <scheme>://<server>/
2205 */
2206 p = strchr(p, ':');
2207 if (!p || (*++p != '/') || (*++p != '/')) {
2208 malformed:
2209 REDEBUG("URI \"%s\" is malformed, can't find start of path", uri);
2210 return -1;
2211 }
2212 p = strchr(p + 1, '/');
2213 if (!p) {
2214 goto malformed;
2215 }
2216
2217 len = (p - uri);
2218
2219 /*
2220 * Unescape any special sequences in the first part of the URI
2221 */
2222 scheme = curl_easy_unescape(candle, uri, (int)len, NULL);
2223 if (!scheme) {
2224 REDEBUG("Error unescaping host");
2225 return -1;
2226 }
2227
2228 /*
2229 * URIs can't contain spaces, so anything after the space must
2230 * be something else.
2231 */
2232 q = strchr(p, ' ');
2233 *out = q ? talloc_typed_asprintf(request, "%s%.*s", scheme, (int)(q - p), p) :
2234 talloc_typed_asprintf(request, "%s%s", scheme, p);
2235
2236 MEM(*out);
2237 curl_free(scheme);
2238
2239 return talloc_strlen(*out); /* array_length includes \0 */
2240}
static int const char char buffer[256]
Definition acutest.h:641
static uint8_t request[4]
#define RCSID(id)
Definition build.h:603
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:245
#define unlikely(_x)
Definition build.h:486
#define UNUSED
Definition build.h:415
#define NUM_ELEMENTS(_t)
Definition build.h:437
#define FR_CURL_REQUEST_SET_OPTION(_x, _y)
Definition base.h:65
fr_curl_io_request_t * fr_curl_io_request_alloc(TALLOC_CTX *ctx)
Allocate a new curl easy request and wrapper struct.
Definition io.c:540
void * uctx
Private data for the module using the API.
Definition base.h:103
CURL * candle
Request specific handle.
Definition base.h:100
Structure representing an individual request being passed to curl for processing.
Definition base.h:99
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition dcursor.h:288
static void * fr_dcursor_remove(fr_dcursor_t *cursor)
Remove the current item.
Definition dcursor.h:480
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition dcursor.h:337
#define MEM(x)
Definition debug.h:38
#define RADIUSD_VERSION_STRING
Definition dependency.h:39
Test enumeration values.
Definition dict_test.h:92
talloc_free(hp)
char const * unlang_interpret_virtual_server(request_t *request)
Return the current virtual server for this request.
Definition interpret.c:2451
char * fr_json_afrom_pair_list(TALLOC_CTX *ctx, fr_pair_list_t *vps, fr_json_format_t const *format)
Returns a JSON string of a list of value pairs.
Definition json.c:1330
int fr_curl_easy_tls_init(fr_curl_io_request_t *randle, fr_curl_tls_t const *conf)
Definition base.c:139
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:460
#define RWDEBUG(fmt,...)
Definition log.h:378
#define RDEBUG3(fmt,...)
Definition log.h:360
#define RERROR(fmt,...)
Definition log.h:315
#define RPWDEBUG(fmt,...)
Definition log.h:383
#define RINDENT()
Indent R* messages by one level.
Definition log.h:447
int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t func, void *ctx)
Convert map_t to fr_pair_t (s) and add them to a request_t.
Definition map.c:1846
#define ROUND_UP(_num, _mul)
Round up - Works in all cases, but is slower.
Definition math.h:209
long int ssize_t
unsigned long int size_t
static size_t used
int strncasecmp(char *s1, char *s2, int n)
Definition missing.c:35
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
Temporary structure to hold arguments for module calls.
Definition module_ctx.h:41
int fr_pair_value_from_str(fr_pair_t *vp, char const *value, size_t inlen, fr_sbuff_unescape_rules_t const *uerules, UNUSED bool tainted)
Convert string value to native attribute value.
Definition pair.c:2561
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition pair.c:1306
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition pair.c:284
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:2756
#define fr_assert(_expr)
Definition rad_assert.h:37
#define pair_update_request(_attr, _da)
#define REDEBUG(fmt,...)
#define RDEBUG2(fmt,...)
fr_dict_attr_t const * request_attr_reply
Definition request.c:44
static int rest_decode_plain(UNUSED rlm_rest_t const *inst, UNUSED rlm_rest_section_t const *section, request_t *request, UNUSED fr_curl_io_request_t *randle, char *raw, size_t rawlen)
Converts plain response into a single fr_pair_t.
Definition rest.c:652
fr_table_num_sorted_t const http_auth_table[]
Definition rest.c:162
static int json_map_getvalue(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map, void *uctx)
Converts JSON "value" key into fr_pair_t when processing a map_t.
Definition rest.c:851
size_t len
Length of data.
Definition rest.c:212
char const * start
Start of the buffer.
Definition rest.c:210
#define CURLAUTH_DIGEST_IE
Definition rest.c:84
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:1811
ssize_t rest_uri_host_unescape(char **out, UNUSED rlm_rest_t const *inst, request_t *request, fr_curl_io_request_t *randle, char const *uri)
Unescapes the host portion of a URI string.
Definition rest.c:2190
http_body_type_t rest_response_body_type_get(fr_curl_io_request_t *randle)
Return the body type of a HTTP response.
Definition rest.c:1650
static size_t rest_encode_custom(void *out, size_t size, size_t nmemb, void *userdata)
Copies a pre-expanded xlat string to the output buffer.
Definition rest.c:294
static int json_pair_alloc(UNUSED rlm_rest_t const *instance, rlm_rest_section_t const *section, request_t *request, json_object *object, UNUSED int level, int max)
Processes JSON response and converts it into multiple fr_pair_ts.
Definition rest.c:986
fr_table_num_sorted_t const http_body_type_table[]
Conversion table for type config values.
Definition rest.c:146
static size_t rest_encode_json(void *out, size_t size, size_t nmemb, void *userdata)
Encodes fr_pair_t linked list in JSON format.
Definition rest.c:530
#define CURLOPT_TLSAUTH_SRP
Definition rest.c:75
int do_xlat
If true value will be expanded with xlat.
Definition rest.c:226
#define CURLAUTH_BASIC
Definition rest.c:78
json_object * value
Object to extract value from.
Definition rest.c:229
fr_table_num_sorted_t const http_method_table[]
Conversion table for method config values.
Definition rest.c:127
void * rest_mod_conn_create(TALLOC_CTX *ctx, void *instance, UNUSED fr_time_delta_t timeout)
Creates a new connection handle for use by the FR connection API.
Definition rest.c:259
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:1634
static bool rest_request_contains_header(fr_curl_io_request_t *randle, char const *header)
See if the list of headers already contains a header.
Definition rest.c:1766
static void rest_request_init(rlm_rest_section_t const *section, request_t *request, rlm_rest_request_t *ctx)
(Re-)Initialises the data in a rlm_rest_request_t.
Definition rest.c:629
static ssize_t rest_request_encode_wrapper(char **out, UNUSED rlm_rest_t const *inst, rest_read_t func, size_t limit, void *userdata)
Emulates successive libcurl calls to an encoding function.
Definition rest.c:593
static int rest_decode_json(rlm_rest_t const *instance, rlm_rest_section_t const *section, request_t *request, UNUSED fr_curl_io_request_t *randle, char *raw, UNUSED size_t rawlen)
Converts JSON response into fr_pair_ts and adds them to the request.
Definition rest.c:1196
void rest_response_debug(request_t *request, fr_curl_io_request_t *handle)
Print out the response text.
Definition rest.c:1577
#define CURLAUTH_NTLM
Definition rest.c:90
fr_table_num_sorted_t const http_content_type_table[]
Conversion table for "Content-Type" header values.
Definition rest.c:190
#define SET_AUTH_OPTION(_x, _y)
const unsigned long http_curl_auth[REST_HTTP_AUTH_NUM_ENTRIES]
Definition rest.c:101
#define CURLAUTH_NTLM_WB
Definition rest.c:93
static int rest_decode_post(UNUSED rlm_rest_t const *instance, rlm_rest_section_t const *section, request_t *request, fr_curl_io_request_t *randle, char *raw, size_t rawlen)
Converts POST response into fr_pair_ts and adds them to the request.
Definition rest.c:696
const http_body_type_t http_body_type_supported[REST_HTTP_BODY_NUM_ENTRIES]
Table of encoder/decoder support.
Definition rest.c:44
#define CURLAUTH_GSSNEGOTIATE
Definition rest.c:87
int is_json
If true value will be inserted as raw JSON (multiple values not supported).
Definition rest.c:227
size_t rest_uri_escape(UNUSED request_t *request, char *out, size_t outlen, char const *raw, UNUSED void *arg)
URL encodes a string.
Definition rest.c:2164
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:1731
void rest_response_error(request_t *request, fr_curl_io_request_t *handle)
Print out the response text as error lines.
Definition rest.c:1552
size_t http_body_type_table_len
Definition rest.c:160
static int rest_request_config_body(module_ctx_t const *mctx, rlm_rest_section_t const *section, request_t *request, fr_curl_io_request_t *randle, rest_read_t func)
Configures body specific curlopts.
Definition rest.c:1673
static int _mod_conn_free(fr_curl_io_request_t *randle)
Frees a libcurl handle, and any additional memory used by context data.
Definition rest.c:238
static void rest_response_init(rlm_rest_section_t const *section, request_t *request, rlm_rest_response_t *ctx, http_body_type_t type, tmpl_t *header)
(Re-)Initialises the data in a rlm_rest_response_t.
Definition rest.c:1612
static size_t rest_response_body(void *in, size_t size, size_t nmemb, void *userdata)
Processes incoming HTTP body data from libcurl.
Definition rest.c:1470
static size_t rest_encode_post(void *out, size_t size, size_t nmemb, void *userdata)
Encodes fr_pair_t linked list in POST format.
Definition rest.c:353
char const * p
how much text we've sent so far.
Definition rest.c:211
size_t http_auth_table_len
Definition rest.c:174
static size_t rest_response_header(void *in, size_t size, size_t nmemb, void *userdata)
Processes incoming HTTP header data from libcurl.
Definition rest.c:1244
size_t http_content_type_table_len
Definition rest.c:203
#define CURLAUTH_DIGEST
Definition rest.c:81
const bool http_body_type_binary[REST_HTTP_BODY_NUM_ENTRIES]
Table of which known body types are expected to give binary data.
Definition rest.c:66
size_t http_method_table_len
Definition rest.c:135
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:2111
Flags to control the conversion of JSON values to fr_pair_ts.
Definition rest.c:225
Function prototypes and datatypes for the REST (HTTP) transport.
rlm_rest_t const * instance
This instance of rlm_rest.
Definition rest.h:259
read_state_t state
Encoder state.
Definition rest.h:245
HIDDEN fr_dict_attr_t const * attr_rest_http_header
Definition rlm_rest.c:276
bool is_json
Section default for the per-attribute "is_json" flag in JSON responses.
Definition rest.h:131
http_auth_type_t auth
HTTP auth type.
Definition rest.h:120
struct curl_slist * headers
Any HTTP headers which will be sent with the request.
Definition rest.h:283
rlm_rest_section_response_json_t json
Configuration for the JSON decoder.
Definition rest.h:151
tmpl_t * header
Where to create pairs representing HTTP response headers.
Definition rest.h:273
http_method_t
Definition rest.h:41
@ REST_HTTP_METHOD_PATCH
Definition rest.h:46
@ REST_HTTP_METHOD_DELETE
Definition rest.h:47
@ REST_HTTP_METHOD_PUT
Definition rest.h:45
@ REST_HTTP_METHOD_POST
Definition rest.h:44
@ REST_HTTP_METHOD_UNKNOWN
Definition rest.h:42
@ REST_HTTP_METHOD_CUSTOM
Must always come last, should not be in method table.
Definition rest.h:48
@ REST_HTTP_METHOD_GET
Definition rest.h:43
request_t * request
Current request.
Definition rest.h:262
char * buffer
Raw incoming HTTP data.
Definition rest.h:265
int code
HTTP Status Code.
Definition rest.h:269
rlm_rest_section_response_post_t post
Configuration for the POST decoder.
Definition rest.h:152
write_state_t state
Decoder state.
Definition rest.h:263
bool fail_header_decode
Force header decoding to fail for debugging purposes.
Definition rest.h:199
char const * proxy
Send request via this proxy.
Definition rest.h:110
size_t used
Space used in buffer.
Definition rest.h:267
http_body_type_t type
HTTP Content Type.
Definition rest.h:270
char * body
Pointer to the buffer which contains body data/ Only used when not performing chunked encoding.
Definition rest.h:286
fr_curl_tls_t tls
Definition rest.h:167
#define REST_BODY_MAX_ATTRS
Definition rest.h:39
bool fail_body_decode
Force body decoding to fail for debugging purposes.
Definition rest.h:200
http_body_type_t force_to
Override the Content-Type header in the response to force decoding as a particular type.
Definition rest.h:145
size_t max_body_in
Maximum size of incoming data.
Definition rest.h:149
fr_dcursor_t cursor
Cursor pointing to the start of the list to encode.
Definition rest.h:247
http_body_type_t force_to
Force decoding the body type as a particular encoding.
Definition rest.h:271
char const * method_str
The string version of the HTTP method.
Definition rest.h:112
http_auth_type_t
Definition rest.h:68
@ REST_HTTP_AUTH_NTLM_WB
Definition rest.h:77
@ REST_HTTP_AUTH_NUM_ENTRIES
Definition rest.h:80
@ REST_HTTP_AUTH_BASIC
Definition rest.h:72
@ REST_HTTP_AUTH_NTLM
Definition rest.h:76
@ REST_HTTP_AUTH_DIGEST
Definition rest.h:73
@ REST_HTTP_AUTH_TLS_SRP
Definition rest.h:71
@ REST_HTTP_AUTH_UNKNOWN
Definition rest.h:69
@ REST_HTTP_AUTH_GSSNEGOTIATE
Definition rest.h:75
@ REST_HTTP_AUTH_ANY
Definition rest.h:78
@ REST_HTTP_AUTH_NONE
Definition rest.h:70
@ REST_HTTP_AUTH_DIGEST_IE
Definition rest.h:74
@ REST_HTTP_AUTH_ANY_SAFE
Definition rest.h:79
rlm_rest_section_t const * section
Section configuration.
Definition rest.h:260
@ READ_STATE_ATTR_CONT
Definition rest.h:223
@ READ_STATE_ATTR_BEGIN
Definition rest.h:222
@ READ_STATE_END
Definition rest.h:224
@ READ_STATE_INIT
Definition rest.h:221
struct rlm_rest_call_env_t::@212 response
char const * body_str
The string version of the encoding/content type.
Definition rest.h:115
#define REST_BODY_ALLOC_CHUNK
Definition rest.h:38
HIDDEN fr_dict_attr_t const * attr_rest_http_body
Definition rlm_rest.c:275
struct rlm_rest_call_env_t::@211 request
rlm_rest_section_request_t request
Request configuration.
Definition rest.h:164
rlm_rest_response_t response
Response context data.
Definition rest.h:290
size_t(* rest_read_t)(void *ptr, size_t size, size_t nmemb, void *userdata)
Definition rest.h:326
bool do_xlat
If true, values in POST responses are xlat expanded before being assigned to attributes.
Definition rest.h:138
#define REST_BODY_MAX_LEN
Definition rest.h:37
rlm_rest_section_t const * section
Section configuration.
Definition rest.h:242
void * encoder
Encoder specific data.
Definition rest.h:251
bool accept_all
Accept all content types.
Definition rest.h:147
rlm_rest_request_t request
Request context data.
Definition rest.h:289
rlm_rest_section_response_t response
Response configuration.
Definition rest.h:165
fr_time_delta_t timeout
Timeout timeval.
Definition rest.h:161
bool do_xlat
Section default for the per-attribute "do_xlat" flag in JSON responses.
Definition rest.h:128
char const * name
Section name.
Definition rest.h:159
size_t alloc
Space allocated for buffer.
Definition rest.h:266
request_t * request
Current request.
Definition rest.h:244
http_body_type_t
Definition rest.h:51
@ REST_HTTP_BODY_HTML
Definition rest.h:62
@ REST_HTTP_BODY_PLAIN
Definition rest.h:63
@ REST_HTTP_BODY_JSON
Definition rest.h:59
@ REST_HTTP_BODY_INVALID
Definition rest.h:55
@ REST_HTTP_BODY_XML
Definition rest.h:60
@ REST_HTTP_BODY_UNSUPPORTED
Definition rest.h:53
@ REST_HTTP_BODY_YAML
Definition rest.h:61
@ REST_HTTP_BODY_POST
Definition rest.h:58
@ REST_HTTP_BODY_CUSTOM
Definition rest.h:57
@ REST_HTTP_BODY_NUM_ENTRIES
Definition rest.h:65
@ REST_HTTP_BODY_CRL
Definition rest.h:64
@ REST_HTTP_BODY_UNKNOWN
Definition rest.h:52
@ REST_HTTP_BODY_NONE
Definition rest.h:56
@ REST_HTTP_BODY_UNAVAILABLE
Definition rest.h:54
rlm_rest_t const * instance
This instance of rlm_rest.
Definition rest.h:241
char const * rest_no_proxy
Magic pointer value for determining if we should disable proxying.
Definition rlm_rest.c:78
uint32_t chunk
Max chunk-size (mainly for testing the encoders)
Definition rest.h:124
size_t chunk
Chunk size.
Definition rest.h:249
@ WRITE_STATE_INIT
Definition rlm_ftp.c:61
@ WRITE_STATE_PARSE_HEADERS
Definition rlm_ocsp.c:63
@ WRITE_STATE_PARSE_CONTENT
Definition rlm_ocsp.c:64
static char const * name
#define FR_SBUFF_IN(_start, _len_or_end)
#define FR_SBUFF_OUT(_start, _len_or_end)
void * data
Module's instance data.
Definition module.h:297
static fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
Definition tmpl.h:929
fr_pair_list_t * tmpl_list_head(request_t *request, fr_dict_attr_t const *list)
Resolve attribute fr_pair_list_t value to an attribute list.
Definition tmpl_eval.c:70
TALLOC_CTX * tmpl_list_ctx(request_t *request, fr_dict_attr_t const *list)
Return the correct TALLOC_CTX to alloc fr_pair_t in, for a list.
Definition tmpl_eval.c:110
ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_token_t quote, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Convert an arbitrary string into a tmpl_t.
int tmpl_request_ptr(request_t **request, FR_DLIST_HEAD(tmpl_request_list) const *rql)
Resolve a tmpl_request_ref_t to a request_t.
Definition tmpl_eval.c:163
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:826
fr_slen_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, tmpl_attr_error_t *err, tmpl_t **out, char const *name, tmpl_rules_t const *rules))
Parse a string into a TMPL_TYPE_ATTR_* type tmpl_t.
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:343
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:37
#define fr_skip_whitespace(_p)
Skip whitespace ('\t', '\n', '\v', '\f', '\r', ' ')
Definition skip.h:36
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
return count
Definition module.c:155
eap_aka_sim_process_conf_t * inst
fr_aka_sim_id_type_t type
fr_pair_t * vp
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
Value pair map.
Definition map.h:77
fr_token_t op
The operator that controls insertion of the dst attribute.
Definition map.h:82
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition map.h:78
map_list_t child
parent map, for nested ones
Definition map.h:89
tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition map.h:79
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition pair.h:69
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition table.h:685
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:804
#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:725
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
char * talloc_bstr_realloc(TALLOC_CTX *ctx, char *in, size_t inlen)
Trim a bstr (char) buffer.
Definition talloc.c:757
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition talloc.c:621
#define talloc_get_type_abort_const
Definition talloc.h:117
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:143
static int64_t fr_time_delta_to_msec(fr_time_delta_t delta)
Definition time.h:646
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
fr_table_num_ordered_t const fr_tokens_table[]
Definition token.c:33
@ T_SINGLE_QUOTED_STRING
Definition token.h:120
@ T_BARE_WORD
Definition token.h:118
@ T_OP_EQ
Definition token.h:81
@ T_OP_SET
Definition token.h:82
@ T_OP_ADD_EQ
Definition token.h:67
ssize_t xlat_aeval(TALLOC_CTX *ctx, char **out, request_t *request, char const *fmt, xlat_escape_legacy_t escape, void const *escape_ctx))
Definition xlat_eval.c:1916
#define fr_pair_dcursor_by_da_init(_cursor, _list, _da)
Initialise a cursor that will return only attributes matching the specified fr_dict_attr_t.
Definition pair.h:640
ssize_t fr_pair_print_value_quoted(fr_sbuff_t *out, fr_pair_t const *vp, fr_token_t quote)
Print the value of an attribute to a string.
Definition pair_print.c:59
#define fr_pair_dcursor_init(_cursor, _list)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
Definition pair.h:605
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:454
fr_slen_t fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, char const *in, size_t inlen, fr_sbuff_unescape_rules_t const *erules)
Definition value.c:6256
int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_value_box_t const *src)
Convert one type of fr_value_box_t to another.
Definition value.c:4071
void fr_value_box_strdup_shallow(fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Assign a buffer containing a nul terminated string to a box, but don't copy it.
Definition value.c:4873
void fr_value_box_bstrndup_shallow(fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, size_t len, bool tainted)
Assign a string to to a fr_value_box_t.
Definition value.c:5101
static fr_sbuff_err_t char size_t fr_sbuff_t * in
Definition value.h:1061
static fr_sbuff_err_t char size_t fr_sbuff_t size_t max
Definition value.h:1061
static fr_slen_t data
Definition value.h:1366
#define FR_VALUE_BOX_INITIALISER_NULL(_vb)
A static initialiser for stack/globally allocated boxes.
Definition value.h:542
#define fr_box_strvalue_len(_val, _len)
Definition value.h:340
#define fr_value_box_init_null(_vb)
Initialise an empty/null box that will be filled later.
Definition value.h:640
static fr_sbuff_err_t char ** out
Definition value.h:1061
static fr_sbuff_err_t char size_t * len
Definition value.h:1061
#define fr_box_strvalue(_val)
Definition value.h:339
#define fr_box_time_delta(_val)
Definition value.h:397
#define fr_value_box(_box, _var, _tainted)
Automagically fill in a box, determining the value type from the type of the C variable.
Definition value.h:935
#define fr_value_box_list_foreach(_list_head, _iter)
Definition value.h:253