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.
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.
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
21 * @brief String expansion ("translation"). Implements %Attribute -> value
23 * @copyright 2000,2006 The FreeRADIUS server project
24 * @copyright 2000 Alan DeKok <aland@ox.org>
29 #include <freeradius-devel/radiusd.h>
30 #include <freeradius-devel/parser.h>
31 #include <freeradius-devel/rad_assert.h>
32 #include <freeradius-devel/base64.h>
36 typedef struct xlat_t {
37 char name[MAX_STRING_LEN]; //!< Name of the xlat expansion.
38 int length; //!< Length of name.
39 void *instance; //!< Module instance passed to xlat and escape functions.
40 RAD_XLAT_FUNC func; //!< xlat function.
41 RADIUS_ESCAPE_STRING escape; //!< Escape function to apply to dynamic input to func.
42 bool internal; //!< If true, cannot be redefined.
46 XLAT_LITERAL, //!< Literal string
47 XLAT_PERCENT, //!< Literal string with %v
48 XLAT_MODULE, //!< xlat module
49 XLAT_VIRTUAL, //!< virtual attribute
50 XLAT_ATTRIBUTE, //!< xlat attribute
52 XLAT_REGEX, //!< regex reference
54 XLAT_ALTERNATE //!< xlat conditional syntax :-
58 char const *fmt; //!< The format string.
59 size_t len; //!< Length of the format string.
61 xlat_state_t type; //!< type of this expansion.
62 xlat_exp_t *next; //!< Next in the list.
64 xlat_exp_t *child; //!< Nested expansion.
65 xlat_exp_t *alternate; //!< Alternative expansion if this one expanded to a zero length string.
67 value_pair_tmpl_t attr; //!< An attribute template.
68 xlat_t const *xlat; //!< The xlat expansion to expand format with.
71 typedef struct xlat_out {
72 char const *out; //!< Output data.
73 size_t len; //!< Length of the output string.
76 static rbtree_t *xlat_root = NULL;
79 static char const * const xlat_foreach_names[] = {"Foreach-Variable-0",
92 #if REQUEST_MAX_REGEX > 8
93 # error Please fix the following line
95 static int xlat_inst[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; /* up to 8 for regex */
97 char const *radiusd_short_version = RADIUSD_VERSION_STRING;
99 /** Print length of its RHS.
102 static ssize_t xlat_strlen(UNUSED void *instance, UNUSED REQUEST *request,
103 char const *fmt, char *out, size_t outlen)
105 snprintf(out, outlen, "%u", (unsigned int) strlen(fmt));
109 /** Print the size of the attribute in bytes.
112 static ssize_t xlat_length(UNUSED void *instance, UNUSED REQUEST *request,
113 char const *fmt, char *out, size_t outlen)
116 while (isspace((int) *fmt)) fmt++;
118 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
123 snprintf(out, outlen, "%zu", vp->length);
127 /** Print data as integer, not as VALUE.
130 static ssize_t xlat_integer(UNUSED void *instance, REQUEST *request,
131 char const *fmt, char *out, size_t outlen)
135 uint64_t int64 = 0; /* Needs to be initialised to zero */
136 uint32_t int32 = 0; /* Needs to be initialised to zero */
138 while (isspace((int) *fmt)) fmt++;
140 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
145 switch (vp->da->type) {
148 if (vp->length > 8) {
152 if (vp->length > 4) {
153 memcpy(&int64, vp->vp_octets, vp->length);
154 return snprintf(out, outlen, "%" PRIu64, htonll(int64));
157 memcpy(&int32, vp->vp_octets, vp->length);
158 return snprintf(out, outlen, "%i", htonl(int32));
160 case PW_TYPE_INTEGER64:
161 return snprintf(out, outlen, "%" PRIu64, vp->vp_integer64);
164 * IP addresses are treated specially, as parsing functions assume the value
165 * is bigendian and will convert it for us.
167 case PW_TYPE_IPV4_ADDR:
168 return snprintf(out, outlen, "%u", htonl(vp->vp_ipaddr));
170 case PW_TYPE_IPV4_PREFIX:
171 return snprintf(out, outlen, "%u", htonl((*(uint32_t *)(vp->vp_ipv4prefix + 2))));
173 case PW_TYPE_INTEGER:
175 return snprintf(out, outlen, "%u", vp->vp_integer);
177 return snprintf(out, outlen, "%u", (unsigned int) vp->vp_byte);
179 return snprintf(out, outlen, "%u", (unsigned int) vp->vp_short);
182 * Ethernet is weird... It's network related, so we assume to it should be
185 case PW_TYPE_ETHERNET:
186 memcpy(&int64, &vp->vp_ether, vp->length);
187 return snprintf(out, outlen, "%" PRIu64, htonll(int64));
190 return snprintf(out, outlen, "%i", vp->vp_signed);
192 case PW_TYPE_IPV6_ADDR:
193 return fr_prints_uint128(out, outlen, ntohlll(*(uint128_t const *) &vp->vp_ipv6addr));
195 case PW_TYPE_IPV6_PREFIX:
196 return fr_prints_uint128(out, outlen, ntohlll(*(uint128_t const *) &(vp->vp_ipv6prefix[2])));
202 REDEBUG("Type '%s' of length %zu cannot be converted to integer",
203 fr_int2str(dict_attr_types, vp->da->type, "???"), vp->length);
209 /** Print data as hex, not as VALUE.
212 static ssize_t xlat_hex(UNUSED void *instance, REQUEST *request,
213 char const *fmt, char *out, size_t outlen)
221 while (isspace((int) *fmt)) fmt++;
223 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
228 ret = rad_vp2data(&p, vp);
235 * Don't truncate the data.
237 if ((ret < 0 ) || (outlen < (len * 2))) {
242 for (i = 0; i < len; i++) {
243 snprintf(out + 2*i, 3, "%02x", p[i]);
249 /** Return the tag of an attribute reference
252 static ssize_t xlat_tag(UNUSED void *instance, REQUEST *request,
253 char const *fmt, char *out, size_t outlen)
257 while (isspace((int) *fmt)) fmt++;
259 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
264 if (!vp->da->flags.has_tag || !TAG_VALID(vp->tag)) {
269 return snprintf(out, outlen, "%u", vp->tag);
272 /** Print out attribute info
274 * Prints out all instances of a current attribute, or all attributes in a list.
276 * At higher debugging levels, also prints out alternative decodings of the same
277 * value. This is helpful to determine types for unknown attributes of long
278 * passed vendors, or just crazy/broken NAS.
280 * It's also useful for exposing issues in the packet decoding functions, as in
281 * some cases they get fed random garbage data.
283 * This expands to a zero length string.
285 static ssize_t xlat_debug_attr(UNUSED void *instance, REQUEST *request, char const *fmt,
286 char *out, UNUSED size_t outlen)
288 VALUE_PAIR *vp, **vps;
293 value_pair_tmpl_t vpt;
295 if (!RDEBUG_ENABLED2) {
300 while (isspace((int) *fmt)) fmt++;
302 if (tmpl_from_attr_str(&vpt, fmt, REQUEST_CURRENT, PAIR_LIST_REQUEST) <= 0) {
303 RDEBUG("%s", fr_strerror());
308 if (radius_request(¤t, vpt.tmpl_request) < 0) return -2;
310 vps = radius_list(current, vpt.tmpl_list);
315 RIDEBUG("Attributes matching \"%s\"", fmt);
316 vp = fr_cursor_init(&cursor, vps);
319 vp = fr_cursor_next_by_da(&cursor, vpt.tmpl_da, TAG_ANY);
322 DICT_ATTR *dac = NULL;
325 vp_prints_value(buffer, sizeof(buffer), vp, '\'');
328 if (vp->da->flags.has_tag) {
329 RIDEBUG2("%s:%s:%i %s %s",
330 fr_int2str(pair_lists, vpt.tmpl_list, "<INVALID>"),
333 fr_int2str(fr_tokens, vp->op, "<INVALID>"),
336 RIDEBUG2("%s:%s %s %s",
337 fr_int2str(pair_lists, vpt.tmpl_list, "<INVALID>"),
339 fr_int2str(fr_tokens, vp->op, "<INVALID>"),
344 if (!RDEBUG_ENABLED3) goto next_vp;
348 if (vp->da->vendor) {
349 dv = dict_vendorbyvalue(vp->da->vendor);
350 RDEBUG3("Vendor : %i (%s)", vp->da->vendor, dv ? dv->name : "unknown");
352 RDEBUG3("Type : %s", fr_int2str(dict_attr_types, vp->da->type, "<INVALID>"));
353 RDEBUG3("Length : %zu", vp->length);
357 dac = talloc_memdup(request, vp->da, sizeof(DICT_ATTR));
359 talloc_set_type(dac, DICT_ATTR);
365 vp = fr_cursor_next_by_da(&cursor, vpt.tmpl_da, TAG_ANY);
367 vp = fr_cursor_next(&cursor);
375 /** Prints the current module processing the request
378 static ssize_t xlat_module(UNUSED void *instance, REQUEST *request,
379 UNUSED char const *fmt, char *out, size_t outlen)
381 strlcpy(out, request->module, outlen);
387 /** Implements the Foreach-Variable-X
391 static ssize_t xlat_foreach(void *instance, REQUEST *request,
392 UNUSED char const *fmt, char *out, size_t outlen)
398 * See modcall, "FOREACH" for how this works.
400 pvp = (VALUE_PAIR **) request_data_reference(request, radius_get_vp, *(int*) instance);
406 len = vp_prints_value(out, outlen, *pvp, 0);
407 if (is_truncated(len, outlen)) {
408 RDEBUG("Insufficient buffer space to write foreach value");
416 /** Print data as string, if possible.
418 * If attribute "Foo" is defined as "octets" it will normally
419 * be printed as 0x0a0a0a. The xlat "%{string:Foo}" will instead
422 static ssize_t xlat_string(UNUSED void *instance, REQUEST *request,
423 char const *fmt, char *out, size_t outlen)
430 while (isspace((int) *fmt)) fmt++;
438 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) goto nothing;
440 ret = rad_vp2data(&p, vp);
445 switch (vp->da->type) {
447 len = fr_print_string((char const *) p, vp->length, out, outlen, '\0');
451 len = strlcpy(out, vp->vp_strvalue, outlen);
455 len = fr_print_string((char const *) p, ret, out, outlen, '\0');
462 /** xlat expand string attribute value
465 static ssize_t xlat_xlat(UNUSED void *instance, REQUEST *request,
466 char const *fmt, char *out, size_t outlen)
470 while (isspace((int) *fmt)) fmt++;
478 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) goto nothing;
480 return radius_xlat(out, outlen, request, vp->vp_strvalue, NULL, NULL);
483 /** Dynamically change the debugging level for the current request
487 static ssize_t xlat_debug(UNUSED void *instance, REQUEST *request,
488 char const *fmt, char *out, size_t outlen)
493 * Expand to previous (or current) level
495 snprintf(out, outlen, "%d", request->log.lvl & RAD_REQUEST_OPTION_DEBUG4);
498 * Assume we just want to get the current value and NOT set it to 0
505 request->log.lvl = RAD_REQUEST_OPTION_NONE;
506 request->log.func = NULL;
508 if (level > 4) level = 4;
510 request->log.lvl = level;
511 request->log.func = vradlog_request;
519 * Compare two xlat_t structs, based ONLY on the module name.
521 static int xlat_cmp(void const *one, void const *two)
523 xlat_t const *a = one;
524 xlat_t const *b = two;
526 if (a->length != b->length) {
527 return a->length - b->length;
530 return memcmp(a->name, b->name, a->length);
535 * find the appropriate registered xlat function.
537 static xlat_t *xlat_find(char const *name)
541 strlcpy(my_xlat.name, name, sizeof(my_xlat.name));
542 my_xlat.length = strlen(my_xlat.name);
544 return rbtree_finddata(xlat_root, &my_xlat);
548 /** Register an xlat function.
550 * @param[in] name xlat name.
551 * @param[in] func xlat function to be called.
552 * @param[in] escape function to sanitize any sub expansions passed to the xlat function.
553 * @param[in] instance of module that's registering the xlat function.
554 * @return 0 on success, -1 on failure
556 int xlat_register(char const *name, RAD_XLAT_FUNC func, RADIUS_ESCAPE_STRING escape, void *instance)
562 if (!name || !*name) {
563 DEBUG("xlat_register: Invalid xlat name");
568 * First time around, build up the tree...
570 * FIXME: This code should be hoisted out of this function,
571 * and into a global "initialization". But it isn't critical...
578 xlat_root = rbtree_create(NULL, xlat_cmp, free, RBTREE_FLAG_REPLACE);
580 DEBUG("xlat_register: Failed to create tree");
585 for (i = 0; xlat_foreach_names[i] != NULL; i++) {
586 xlat_register(xlat_foreach_names[i],
587 xlat_foreach, NULL, &xlat_inst[i]);
588 c = xlat_find(xlat_foreach_names[i]);
589 rad_assert(c != NULL);
594 #define XLAT_REGISTER(_x) xlat_register(STRINGIFY(_x), xlat_ ## _x, NULL, NULL); \
595 c = xlat_find(STRINGIFY(_x)); \
596 rad_assert(c != NULL); \
599 XLAT_REGISTER(integer);
600 XLAT_REGISTER(strlen);
601 XLAT_REGISTER(length);
604 XLAT_REGISTER(string);
606 XLAT_REGISTER(module);
607 XLAT_REGISTER(debug_attr);
609 xlat_register("debug", xlat_debug, NULL, &xlat_inst[0]);
610 c = xlat_find("debug");
611 rad_assert(c != NULL);
616 * If it already exists, replace the instance.
618 strlcpy(my_xlat.name, name, sizeof(my_xlat.name));
619 my_xlat.length = strlen(my_xlat.name);
620 c = rbtree_finddata(xlat_root, &my_xlat);
623 DEBUG("xlat_register: Cannot re-define internal xlat");
629 c->instance = instance;
634 * Doesn't exist. Create it.
636 c = rad_malloc(sizeof(*c));
637 memset(c, 0, sizeof(*c));
641 strlcpy(c->name, name, sizeof(c->name));
642 c->length = strlen(c->name);
643 c->instance = instance;
645 node = rbtree_insert_node(xlat_root, c);
654 /** Unregister an xlat function
656 * We can only have one function to call per name, so the passing of "func"
657 * here is extraneous.
659 * @param[in] name xlat to unregister.
660 * @param[in] func unused.
661 * @param[in] instance data.
663 void xlat_unregister(char const *name, UNUSED RAD_XLAT_FUNC func, void *instance)
670 strlcpy(my_xlat.name, name, sizeof(my_xlat.name));
671 my_xlat.length = strlen(my_xlat.name);
673 c = rbtree_finddata(xlat_root, &my_xlat);
676 if (c->instance != instance) return;
678 rbtree_deletebydata(xlat_root, c);
681 static int xlat_unregister_callback(void *instance, void *data)
683 xlat_t *c = (xlat_t *) data;
685 if (c->instance != instance) return 0; /* keep walking */
687 return 2; /* delete it */
690 void xlat_unregister_module(void *instance)
692 rbtree_walk(xlat_root, RBTREE_DELETE_ORDER, xlat_unregister_callback, instance);
696 /** Crappy temporary function to add attribute ref support to xlats
698 * This needs to die, and hopefully will die, when xlat functions accept
699 * xlat node structures.
701 * Provides either a pointer to a buffer which contains the value of the reference VALUE_PAIR
702 * in an architecture independent format. Or a pointer to the start of the fmt string.
704 * The pointer is only guaranteed to be valid between calls to xlat_fmt_to_ref,
705 * and so long as the source VALUE_PAIR is not freed.
707 * @param out where to write a pointer to the buffer to the data the xlat function needs to work on.
708 * @param request current request.
710 * @returns the length of the data or -1 on error.
712 ssize_t xlat_fmt_to_ref(uint8_t const **out, REQUEST *request, char const *fmt)
716 while (isspace((int) *fmt)) fmt++;
719 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
724 return rad_vp2data(out, vp);
727 *out = (uint8_t const *)fmt;
731 /** De-register all xlat functions, used mainly for debugging.
736 rbtree_free(xlat_root);
741 # define XLAT_DEBUG DEBUG3
743 # define XLAT_DEBUG(...)
746 static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
748 static ssize_t xlat_tokenize_literal(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
749 int brace, char const **error);
750 static size_t xlat_process(char **out, REQUEST *request, xlat_exp_t const * const head,
751 RADIUS_ESCAPE_STRING escape, void *escape_ctx);
753 static ssize_t xlat_tokenize_alternation(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
760 rad_assert(fmt[0] == '%');
761 rad_assert(fmt[1] == '{');
762 rad_assert(fmt[2] == '%');
763 rad_assert(fmt[3] == '{');
765 XLAT_DEBUG("ALTERNATE <-- %s", fmt);
767 node = talloc_zero(ctx, xlat_exp_t);
768 node->type = XLAT_ALTERNATE;
771 slen = xlat_tokenize_expansion(node, p, &node->child, error);
774 return slen - (p - fmt);
780 *error = "Expected ':' after first expansion";
787 *error = "Expected '-' after ':'";
793 * Allow the RHS to be empty as a special case.
797 * Hack up an empty string.
799 node->alternate = talloc_zero(node, xlat_exp_t);
800 node->alternate->type = XLAT_LITERAL;
801 node->alternate->fmt = talloc_typed_strdup(node->alternate, "");
805 slen = xlat_tokenize_literal(node, p, &node->alternate, true, error);
808 return slen - (p - fmt);
811 if (!node->alternate) {
813 *error = "Empty expansion is invalid";
823 static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
828 char const *attrname;
831 rad_assert(fmt[0] == '%');
832 rad_assert(fmt[1] == '{');
837 if ((fmt[2] == '%') && (fmt[3] == '{')) {
838 return xlat_tokenize_alternation(ctx, fmt, head, error);
841 XLAT_DEBUG("EXPANSION <-- %s", fmt);
842 node = talloc_zero(ctx, xlat_exp_t);
843 attrname = node->fmt = fmt + 2;
848 * Handle regex's specially.
850 if (isdigit((int) fmt[2]) && (fmt[3] == '}')) {
853 *error = "Invalid regex reference";
857 XLAT_DEBUG("REGEX <-- %s", fmt);
859 node->attr.tmpl_num = fmt[2] - '0'; /* ASCII */
861 node->type = XLAT_REGEX;
865 #endif /* HAVE_REGEX */
870 * %{Tunnel-Password:1}
871 * %{Tunnel-Password:1[#]}
872 * %{request:Attr-Name}
873 * %{request:Tunnel-Password:1}
874 * %{request:Tunnel-Password:1[#]}
878 for (p = fmt + 2; *p != '\0'; p++) {
879 if (*p == ':') break;
881 if (isspace((int) *p)) break;
883 if (*p == '[') break;
885 if (*p == '}') break;
888 if (*p != ':') p = NULL;
891 * Might be a module name reference.
899 node->xlat = xlat_find(node->fmt);
901 node->type = XLAT_MODULE;
903 XLAT_DEBUG("MOD <-- %s ... %s", node->fmt, p + 1);
905 slen = xlat_tokenize_literal(node, p + 1, &node->child, true, error);
908 return slen - (p - fmt);
913 rad_assert(node->next == NULL);
918 * Modules can have '}' in their RHS, so we
919 * didn't check for that until now.
921 * As of now, node->fmt MUST be a reference to an
922 * attribute, however complicated. So it MUST have a closing brace.
924 brace = strchr(p + 1, '}');
925 if (!brace) goto no_brace;
931 * %{Tunnel-Password:1}
932 * %{request:Tunnel-Password:1}
934 * <sigh> The syntax is fairly poor.
936 XLAT_DEBUG("Looking for list in '%s'", attrname);
939 * Not a module. Has to be an attribute
942 * As of v3, we've removed %{request: ..>} as
943 * internally registered xlats.
946 node->attr.tmpl_request = radius_request_name(&attrname, REQUEST_CURRENT);
947 rad_assert(node->attr.tmpl_request != REQUEST_UNKNOWN);
949 node->attr.tmpl_list = radius_list_name(&attrname, PAIR_LIST_REQUEST);
950 if (node->attr.tmpl_list == PAIR_LIST_UNKNOWN) {
952 *error = "Unknown module";
957 * Check for a trailing tag.
959 p = strchr(attrname, ':');
963 brace = strchr(attrname, '}');
967 *error = "No matching closing brace";
968 return -1; /* second character of format string */
972 node->attr.tmpl_request = REQUEST_CURRENT;
973 node->attr.tmpl_list = PAIR_LIST_REQUEST;
978 XLAT_DEBUG("Looking for attribute name in %s", attrname);
981 * Allow for an array reference. They come AFTER the
982 * tag, if the tag exists. Otherwise, they come after
983 * the attribute name.
986 q = strchr(p + 1, '[');
988 q = strchr(attrname, '[');
990 if (q) *(q++) = '\0';
994 *error = "Empty expression is invalid";
995 return -(attrname - fmt);
999 * It's either an attribute name, or a Tunnel-Password:TAG
1000 * with the ':' already set to NULL.
1002 node->attr.tmpl_da = dict_attrbyname(attrname);
1003 if (!node->attr.tmpl_da) {
1005 * Foreach. Maybe other stuff, too.
1007 node->xlat = xlat_find(attrname);
1009 node->type = XLAT_VIRTUAL;
1010 node->fmt = attrname;
1012 XLAT_DEBUG("VIRTUAL <-- %s", node->fmt);
1014 rad_assert(node->next == NULL);
1020 *error = "Unknown attribute";
1021 return -(attrname - fmt);
1031 if (!node->attr.tmpl_da->flags.has_tag) {
1033 *error = "Attribute cannot have a tag";
1037 tag = strtoul(p + 1, &end, 10);
1040 if (tag == ULONG_MAX) {
1042 *error = "Invalid tag value";
1046 node->attr.tmpl_tag = tag;
1051 *error = "Unexpected text after tag";
1056 node->attr.tmpl_tag = TAG_ANY;
1061 * Check for array reference
1069 node->attr.tmpl_num = NUM_COUNT;
1072 } else if (*p == '*') {
1073 node->attr.tmpl_num = NUM_ALL;
1076 } else if (isdigit((int) *p)) {
1077 num = strtoul(p, &end, 10);
1080 *error = "Invalid array index";
1084 node->attr.tmpl_num = num;
1088 *error = "Invalid array index";
1094 *error = "Expected ']'";
1101 *error = "Unexpected text after array reference";
1105 node->attr.tmpl_num = NUM_ANY;
1108 rad_assert(!p || (p == brace));
1110 node->type = XLAT_ATTRIBUTE;
1114 rad_assert(node->next == NULL);
1119 static ssize_t xlat_tokenize_literal(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
1120 int brace, char const **error)
1125 if (!*fmt) return 0;
1127 XLAT_DEBUG("LITERAL <-- %s", fmt);
1129 node = talloc_zero(ctx, xlat_exp_t);
1132 node->type = XLAT_LITERAL;
1140 *error = "Invalid escape at end of string";
1148 * Process the expansion.
1150 if ((p[0] == '%') && (p[1] == '{')) {
1153 XLAT_DEBUG("LITERAL <-- %s", node->fmt);
1155 slen = xlat_tokenize_expansion(node, p, &node->next, error);
1158 return slen - (p - fmt);
1160 *p = '\0'; /* end the literal */
1163 rad_assert(node->next != NULL);
1166 * Short-circuit the recursive call.
1167 * This saves another function call and
1168 * memory allocation.
1173 * "foo %{User-Name} bar"
1175 * EXPANSION User-Name
1178 slen = xlat_tokenize_literal(node->next, p, &(node->next->next), brace, error);
1179 rad_assert(slen != 0);
1182 return slen - (p - fmt);
1186 break; /* stop processing the string */
1190 * Check for valid single-character expansions.
1196 if (!p[1] || !strchr("%dlmtDGHISTYv", p[1])) {
1198 *error = "Invalid variable expansion";
1203 next = talloc_zero(node, xlat_exp_t);
1207 next->fmt = talloc_typed_strdup(next, "%");
1209 XLAT_DEBUG("LITERAL <-- %s", next->fmt);
1210 next->type = XLAT_LITERAL;
1215 XLAT_DEBUG("PERCENT <-- %c", *next->fmt);
1216 next->type = XLAT_PERCENT;
1228 slen = xlat_tokenize_literal(node->next, p, &(node->next->next), brace, error);
1229 rad_assert(slen != 0);
1232 return slen - (p - fmt);
1236 break; /* stop processing the string */
1240 * If required, eat the brace.
1242 if (brace && (*p == '}')) {
1253 * Squash zero-width literals
1255 if (node->len > 0) {
1258 *head = talloc_steal(ctx, node->next);
1266 static char const xlat_tabs[] = " ";
1268 static void xlat_tokenize_debug(xlat_exp_t const *node, int lvl)
1270 rad_assert(node != NULL);
1272 if (lvl >= (int) sizeof(xlat_tabs)) lvl = sizeof(xlat_tabs);
1275 switch (node->type) {
1277 DEBUG("%.*sliteral --> %s", lvl, xlat_tabs, node->fmt);
1281 DEBUG("%.*spercent --> %c", lvl, xlat_tabs, node->fmt[0]);
1284 case XLAT_ATTRIBUTE:
1285 rad_assert(node->attr.tmpl_da != NULL);
1286 DEBUG("%.*sattribute --> %s", lvl, xlat_tabs, node->attr.tmpl_da->name);
1287 rad_assert(node->child == NULL);
1288 if ((node->attr.tmpl_tag != TAG_ANY) || (node->attr.tmpl_num != NUM_ANY)) {
1289 DEBUG("%.*s{", lvl, xlat_tabs);
1291 DEBUG("%.*sref %d", lvl + 1, xlat_tabs, node->attr.tmpl_request);
1292 DEBUG("%.*slist %d", lvl + 1, xlat_tabs, node->attr.tmpl_list);
1294 if (node->attr.tmpl_tag != TAG_ANY) {
1295 DEBUG("%.*stag %d", lvl + 1, xlat_tabs, node->attr.tmpl_tag);
1297 if (node->attr.tmpl_num != NUM_ANY) {
1298 if (node->attr.tmpl_num == NUM_COUNT) {
1299 DEBUG("%.*s[#]", lvl + 1, xlat_tabs);
1300 } else if (node->attr.tmpl_num == NUM_ALL) {
1301 DEBUG("%.*s[*]", lvl + 1, xlat_tabs);
1303 DEBUG("%.*s[%d]", lvl + 1, xlat_tabs, node->attr.tmpl_num);
1307 DEBUG("%.*s}", lvl, xlat_tabs);
1312 rad_assert(node->fmt != NULL);
1313 DEBUG("%.*svirtual --> %s", lvl, xlat_tabs, node->fmt);
1317 rad_assert(node->xlat != NULL);
1318 DEBUG("%.*sxlat --> %s", lvl, xlat_tabs, node->xlat->name);
1320 DEBUG("%.*s{", lvl, xlat_tabs);
1321 xlat_tokenize_debug(node->child, lvl + 1);
1322 DEBUG("%.*s}", lvl, xlat_tabs);
1328 DEBUG("%.*sregex-var --> %d", lvl, xlat_tabs, node->attr.tmpl_num);
1332 case XLAT_ALTERNATE:
1333 DEBUG("%.*sif {", lvl, xlat_tabs);
1334 xlat_tokenize_debug(node->child, lvl + 1);
1335 DEBUG("%.*s}", lvl, xlat_tabs);
1336 DEBUG("%.*selse {", lvl, xlat_tabs);
1337 xlat_tokenize_debug(node->alternate, lvl + 1);
1338 DEBUG("%.*s}", lvl, xlat_tabs);
1345 size_t xlat_sprint(char *buffer, size_t bufsize, xlat_exp_t const *node)
1356 end = buffer + bufsize;
1359 switch (node->type) {
1361 strlcpy(p, node->fmt, end - p);
1367 p[1] = node->fmt[0];
1371 case XLAT_ATTRIBUTE:
1375 if (node->attr.tmpl_request != REQUEST_CURRENT) {
1376 strlcpy(p, fr_int2str(request_refs, node->attr.tmpl_request, "??"), end - p);
1381 if ((node->attr.tmpl_request != REQUEST_CURRENT) ||
1382 (node->attr.tmpl_list != PAIR_LIST_REQUEST)) {
1383 strlcpy(p, fr_int2str(pair_lists, node->attr.tmpl_list, "??"), end - p);
1388 strlcpy(p, node->attr.tmpl_da->name, end - p);
1391 if (node->attr.tmpl_tag != TAG_ANY) {
1393 snprintf(p, end - p, "%u", node->attr.tmpl_tag);
1397 if (node->attr.tmpl_num != NUM_ANY) {
1399 switch (node->attr.tmpl_num) {
1409 snprintf(p, end - p, "%i", node->attr.tmpl_num);
1418 snprintf(p, end - p, "%%{%i}", node->attr.tmpl_num);
1425 strlcpy(p, node->fmt, end - p);
1433 strlcpy(p, node->xlat->name, end - p);
1436 rad_assert(node->child != NULL);
1437 len = xlat_sprint(p, end - p, node->child);
1442 case XLAT_ALTERNATE:
1446 len = xlat_sprint(p, end - p, node->child);
1452 len = xlat_sprint(p, end - p, node->alternate);
1460 if (p == end) break;
1470 ssize_t xlat_tokenize(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
1473 return xlat_tokenize_literal(ctx, fmt, head, false, error);
1477 /** Tokenize an xlat expansion
1479 * @param[in] request the input request. Memory will be attached here.
1480 * @param[in] fmt the format string to expand
1481 * @param[out] head the head of the xlat list / tree structure.
1483 static ssize_t xlat_tokenize_request(REQUEST *request, char const *fmt, xlat_exp_t **head)
1492 * Copy the original format string to a buffer so that
1493 * the later functions can mangle it in-place, which is
1496 tokens = talloc_typed_strdup(request, fmt);
1497 if (!tokens) return -1;
1499 slen = xlat_tokenize_literal(request, tokens, head, false, &error);
1502 * Zero length expansion, return a zero length node.
1505 *head = talloc_zero(request, xlat_exp_t);
1509 * Output something like:
1512 * " ^ error was here"
1515 talloc_free(tokens);
1516 rad_assert(error != NULL);
1518 REMARKER(fmt, -slen, error);
1522 if (*head && (debug_flag > 2)) {
1524 DEBUG("Parsed xlat tree:");
1525 xlat_tokenize_debug(*head, 0);
1529 * All of the nodes point to offsets in the "tokens"
1530 * string. Let's ensure that free'ing head will free
1533 (void) talloc_steal(*head, tokens);
1539 static char *xlat_getvp(TALLOC_CTX *ctx, REQUEST *request, pair_lists_t list, DICT_ATTR const *da,
1540 int8_t tag, int num, bool return_null)
1542 VALUE_PAIR *vp, *vps = NULL, *myvp = NULL;
1543 RADIUS_PACKET *packet = NULL;
1548 * Arg. Too much abstraction is annoying.
1552 if (return_null) return NULL;
1553 return vp_aprint_type(ctx, da->type);
1555 case PAIR_LIST_CONTROL:
1556 vps = request->config_items;
1559 case PAIR_LIST_REQUEST:
1560 packet = request->packet;
1561 if (packet) vps = packet->vps;
1564 case PAIR_LIST_REPLY:
1565 packet = request->reply;
1566 if (packet) vps = packet->vps;
1570 case PAIR_LIST_PROXY_REQUEST:
1571 packet = request->proxy;
1572 if (packet) vps = packet->vps;
1575 case PAIR_LIST_PROXY_REPLY:
1576 packet = request->proxy_reply;
1577 if (packet) vps = packet->vps;
1584 if (request->coa) packet = request->coa->packet;
1585 if (packet) vps = packet->vps;
1588 case PAIR_LIST_COA_REPLY:
1589 case PAIR_LIST_DM_REPLY:
1590 if (request->coa) packet = request->coa->reply;
1591 if (packet) vps = packet->vps;
1598 * Now we have the list, check to see if we have an attribute in
1599 * the request, if we do, it takes precedence over the virtual
1602 * This allows users to manipulate virtual attributes as if they
1605 vp = pairfind(vps, da->attr, da->vendor, tag);
1606 if (vp) goto do_print;
1609 * We didn't find the VP in a list. It MIGHT be a
1610 * virtual one, in which case we do lots more checks
1611 * below. However, if we're looking for a normal
1612 * attribute, it must exist, and therefore not finding it
1613 * means we return NULL.
1615 if (!da->flags.virtual) return NULL;
1618 * Some non-packet expansions
1622 break; /* ignore them */
1624 case PW_CLIENT_SHORTNAME:
1625 if (num == NUM_COUNT) goto count;
1626 if (request->client && request->client->shortname) {
1627 return talloc_typed_strdup(ctx, request->client->shortname);
1629 return talloc_typed_strdup(ctx, "<UNKNOWN-CLIENT>");
1631 case PW_REQUEST_PROCESSING_STAGE:
1632 if (num == NUM_COUNT) goto count;
1633 if (request->component) {
1634 return talloc_typed_strdup(ctx, request->component);
1636 return talloc_typed_strdup(ctx, "server_core");
1638 case PW_VIRTUAL_SERVER:
1639 if (num == NUM_COUNT) goto count;
1640 if (!request->server) return NULL;
1641 return talloc_typed_strdup(ctx, request->server);
1643 case PW_MODULE_RETURN_CODE:
1644 if (num == NUM_COUNT) goto count;
1645 if (!request->rcode) return NULL;
1646 return talloc_typed_strdup(ctx, fr_int2str(modreturn_table, request->rcode, ""));
1650 * All of the attributes must now refer to a packet.
1651 * If there's no packet, we can't print any attribute
1655 if (return_null) return NULL;
1656 return vp_aprint_type(ctx, da->type);
1664 case PW_PACKET_TYPE:
1665 dv = dict_valbyattr(PW_PACKET_TYPE, 0, packet->code);
1666 if (dv) return talloc_typed_strdup(ctx, dv->name);
1667 return talloc_typed_asprintf(ctx, "%d", packet->code);
1669 case PW_RESPONSE_PACKET_TYPE:
1674 if (request->proxy_reply && (!request->reply || !request->reply->code)) {
1675 code = request->proxy_reply->code;
1678 if (request->reply) {
1679 code = request->reply->code;
1682 return talloc_typed_strdup(ctx, fr_packet_codes[code]);
1686 * Virtual attributes which require a temporary VALUE_PAIR
1687 * to be allocated. We can't use stack allocated memory
1688 * because of the talloc checks sprinkled throughout the
1689 * various VP functions.
1691 case PW_PACKET_AUTHENTICATION_VECTOR:
1692 myvp = pairalloc(ctx, da);
1693 pairmemcpy(myvp, packet->vector, sizeof(packet->vector));
1697 case PW_CLIENT_IP_ADDRESS:
1698 case PW_PACKET_SRC_IP_ADDRESS:
1699 if (packet->src_ipaddr.af == AF_INET) {
1700 myvp = pairalloc(ctx, da);
1701 myvp->vp_ipaddr = packet->src_ipaddr.ipaddr.ip4addr.s_addr;
1706 case PW_PACKET_DST_IP_ADDRESS:
1707 if (packet->dst_ipaddr.af == AF_INET) {
1708 myvp = pairalloc(ctx, da);
1709 myvp->vp_ipaddr = packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
1714 case PW_PACKET_SRC_IPV6_ADDRESS:
1715 if (packet->src_ipaddr.af == AF_INET6) {
1716 myvp = pairalloc(ctx, da);
1717 memcpy(&myvp->vp_ipv6addr,
1718 &packet->src_ipaddr.ipaddr.ip6addr,
1719 sizeof(packet->src_ipaddr.ipaddr.ip6addr));
1724 case PW_PACKET_DST_IPV6_ADDRESS:
1725 if (packet->dst_ipaddr.af == AF_INET6) {
1726 myvp = pairalloc(ctx, da);
1727 memcpy(&myvp->vp_ipv6addr,
1728 &packet->dst_ipaddr.ipaddr.ip6addr,
1729 sizeof(packet->dst_ipaddr.ipaddr.ip6addr));
1734 case PW_PACKET_SRC_PORT:
1735 myvp = pairalloc(ctx, da);
1736 myvp->vp_integer = packet->src_port;
1740 case PW_PACKET_DST_PORT:
1741 myvp = pairalloc(ctx, da);
1742 myvp->vp_integer = packet->dst_port;
1748 * Fake various operations for virtual attributes.
1751 if (num != NUM_ANY) switch (num) {
1753 * [n] is NULL (we only have [0])
1758 * [*] means only one.
1764 * [#] means 1 (as there's only one)
1768 ret = talloc_strdup(ctx, "1");
1772 * [0] is fine (get the first instance)
1782 * We want the N'th VP.
1784 if (num != NUM_ANY) {
1790 * Return a count of the VPs.
1793 fr_cursor_init(&cursor, &vp);
1794 while (fr_cursor_next_by_da(&cursor, da, tag) != NULL) {
1797 return talloc_typed_asprintf(ctx, "%d", count);
1800 * Ugly, but working.
1806 (void) fr_cursor_init(&cursor, &vp);
1807 vp = fr_cursor_next_by_da(&cursor, da, tag);
1808 if (!vp) return NULL;
1810 p = vp_aprint_value(ctx, vp, '"');
1811 if (!p) return NULL;
1812 while ((vp = fr_cursor_next_by_da(&cursor, da, tag)) != NULL) {
1813 q = vp_aprint_value(ctx, vp, '"');
1814 if (!q) return NULL;
1815 p = talloc_strdup_append(p, ",");
1816 p = talloc_strdup_append(p, q);
1823 fr_cursor_init(&cursor, &vp);
1824 while ((vp = fr_cursor_next_by_da(&cursor, da, tag)) != NULL) {
1825 if (count++ == num) break;
1832 if (return_null) return NULL;
1833 return vp_aprint_type(ctx, da->type);
1837 ret = vp_aprint_value(ctx, vp, '"');
1845 static const char xlat_spaces[] = " ";
1848 static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * const node,
1849 RADIUS_ESCAPE_STRING escape, void *escape_ctx, int lvl)
1852 char *str = NULL, *child;
1857 XLAT_DEBUG("%.*sxlat aprint %d", lvl, xlat_spaces, node->type);
1859 switch (node->type) {
1861 * Don't escape this.
1864 XLAT_DEBUG("xlat_aprint LITERAL");
1865 return talloc_typed_strdup(ctx, node->fmt);
1868 * Do a one-character expansion.
1873 size_t freespace = 256;
1877 XLAT_DEBUG("xlat_aprint PERCENT");
1879 str = talloc_array(ctx, char, freespace); /* @todo do better allocation */
1882 when = request->timestamp;
1883 if (request->packet) {
1884 when = request->packet->timestamp.tv_sec;
1893 case 'd': /* request day */
1894 (void) localtime_r(&when, &ts);
1895 strftime(str, freespace, "%d", &ts);
1898 case 'l': /* request timestamp */
1899 snprintf(str, freespace, "%lu",
1900 (unsigned long) when);
1903 case 'm': /* request month */
1904 (void) localtime_r(&when, &ts);
1905 strftime(str, freespace, "%m", &ts);
1908 case 'n': /* Request Number*/
1909 snprintf(str, freespace, "%u", request->number);
1912 case 't': /* request timestamp */
1913 CTIME_R(&when, str, freespace);
1914 nl = strchr(str, '\n');
1918 case 'D': /* request date */
1919 (void) localtime_r(&when, &ts);
1920 strftime(str, freespace, "%Y%m%d", &ts);
1923 case 'G': /* request minute */
1924 (void) localtime_r(&when, &ts);
1925 strftime(str, freespace, "%M", &ts);
1928 case 'H': /* request hour */
1929 (void) localtime_r(&when, &ts);
1930 strftime(str, freespace, "%H", &ts);
1933 case 'I': /* Request ID */
1934 if (request->packet) {
1935 snprintf(str, freespace, "%i", request->packet->id);
1939 case 'S': /* request timestamp in SQL format*/
1940 (void) localtime_r(&when, &ts);
1941 strftime(str, freespace, "%Y-%m-%d %H:%M:%S", &ts);
1944 case 'T': /* request timestamp */
1945 (void) localtime_r(&when, &ts);
1946 strftime(str, freespace, "%Y-%m-%d-%H.%M.%S.000000", &ts);
1949 case 'Y': /* request year */
1950 (void) localtime_r(&when, &ts);
1951 strftime(str, freespace, "%Y", &ts);
1954 case 'v': /* Version of code */
1955 snprintf(str, freespace, "%s", radiusd_short_version);
1965 case XLAT_ATTRIBUTE:
1966 XLAT_DEBUG("xlat_aprint ATTRIBUTE");
1968 if (radius_request(&ref, node->attr.tmpl_request) < 0) {
1973 * Some attributes are virtual <sigh>
1975 str = xlat_getvp(ctx, ref, node->attr.tmpl_list, node->attr.tmpl_da, node->attr.tmpl_tag, node->attr.tmpl_num, true);
1977 XLAT_DEBUG("EXPAND attr %s", node->attr.tmpl_da->name);
1978 XLAT_DEBUG(" ---> %s", str);
1983 XLAT_DEBUG("xlat_aprint VIRTUAL");
1984 str = talloc_array(ctx, char, 2048); /* FIXME: have the module call talloc_typed_asprintf */
1985 rcode = node->xlat->func(node->xlat->instance, request, NULL, str, 2048);
1993 XLAT_DEBUG("xlat_aprint MODULE");
1994 if (xlat_process(&child, request, node->child, node->xlat->escape, node->xlat->instance) == 0) {
1998 XLAT_DEBUG("%.*sEXPAND mod %s %s", lvl, xlat_spaces, node->fmt, node->child->fmt);
1999 XLAT_DEBUG("%.*s ---> %s", lvl, xlat_spaces, child);
2004 * The OUTPUT of xlat is a printable string. The INPUT might not be...
2006 * This is really the reverse of fr_print_string().
2010 if (*p == '\\') switch (p[1]) {
2031 str = talloc_array(ctx, char, 2048); /* FIXME: have the module call talloc_typed_asprintf */
2032 *str = '\0'; /* Be sure the string is NULL terminated, we now only free on error */
2034 rcode = node->xlat->func(node->xlat->instance, request, child, str, 2048);
2044 XLAT_DEBUG("xlat_aprint REGEX");
2045 child = request_data_reference(request, request,
2046 REQUEST_DATA_REGEX | node->attr.tmpl_num);
2047 if (!child) return NULL;
2049 str = talloc_typed_strdup(ctx, child);
2053 case XLAT_ALTERNATE:
2054 XLAT_DEBUG("xlat_aprint ALTERNATE");
2055 rad_assert(node->child != NULL);
2056 rad_assert(node->alternate != NULL);
2058 str = xlat_aprint(ctx, request, node->child, escape, escape_ctx, lvl);
2061 str = xlat_aprint(ctx, request, node->alternate, escape, escape_ctx, lvl);
2067 * Escape the non-literals we found above.
2069 if (str && escape) {
2072 escaped = talloc_array(ctx, char, 2048); /* FIXME: do something intelligent */
2073 escape(request, escaped, 2038, str, escape_ctx);
2082 static size_t xlat_process(char **out, REQUEST *request, xlat_exp_t const * const head,
2083 RADIUS_ESCAPE_STRING escape, void *escape_ctx)
2087 char **array, *answer;
2088 xlat_exp_t const *node;
2093 * There are no nodes to process, so the result is a zero
2097 *out = talloc_zero_array(request, char, 1);
2102 * Hack for speed. If it's one expansion, just allocate
2103 * that and return, instead of allocating an intermediary
2108 * Pass the MAIN escape function. Recursive
2109 * calls will call node-specific escape
2112 answer = xlat_aprint(request, request, head, escape, escape_ctx, 0);
2114 *out = talloc_zero_array(request, char, 1);
2118 return strlen(answer);
2121 list = 0; /* FIXME: calculate this once */
2122 for (node = head; node != NULL; node = node->next) {
2126 array = talloc_array(request, char *, list);
2127 if (!array) return -1;
2129 for (node = head, i = 0; node != NULL; node = node->next, i++) {
2130 array[i] = xlat_aprint(array, request, node, escape, escape_ctx, 0); /* may be NULL */
2134 for (i = 0; i < list; i++) {
2135 if (array[i]) total += strlen(array[i]); /* FIXME: calculate strlen once */
2140 *out = talloc_zero_array(request, char, 1);
2144 answer = talloc_array(request, char, total + 1);
2147 for (i = 0; i < list; i++) {
2151 len = strlen(array[i]);
2152 memcpy(answer + total, array[i], len);
2156 answer[total] = '\0';
2157 talloc_free(array); /* and child entries */
2164 /** Replace %whatever in a string.
2166 * See 'doc/variables.txt' for more information.
2168 * @param[out] out Where to write pointer to output buffer.
2169 * @param[in] outlen Size of out.
2170 * @param[in] request current request.
2171 * @param[in] node the xlat structure to expand
2172 * @param[in] escape function to escape final value e.g. SQL quoting.
2173 * @param[in] escape_ctx pointer to pass to escape function.
2174 * @return length of string written @bug should really have -1 for failure
2176 static ssize_t xlat_expand_struct(char **out, size_t outlen, REQUEST *request, xlat_exp_t const *node,
2177 RADIUS_ESCAPE_STRING escape, void *escape_ctx)
2182 rad_assert(node != NULL);
2184 len = xlat_process(&buff, request, node, escape, escape_ctx);
2185 if ((len < 0) || !buff) {
2186 rad_assert(buff == NULL);
2187 if (*out) *out[0] = '\0';
2194 strlcpy(*out, buff, outlen);
2198 return strlen(*out);
2201 static ssize_t xlat_expand(char **out, size_t outlen, REQUEST *request, char const *fmt,
2202 RADIUS_ESCAPE_STRING escape, void *escape_ctx) CC_HINT(nonnull (1, 3, 4));
2204 /** Replace %whatever in a string.
2206 * See 'doc/variables.txt' for more information.
2208 * @param[out] out Where to write pointer to output buffer.
2209 * @param[in] outlen Size of out.
2210 * @param[in] request current request.
2211 * @param[in] fmt string to expand.
2212 * @param[in] escape function to escape final value e.g. SQL quoting.
2213 * @param[in] escape_ctx pointer to pass to escape function.
2214 * @return length of string written @bug should really have -1 for failure
2216 static ssize_t CC_HINT(nonnull (1, 3, 4)) xlat_expand(char **out, size_t outlen, REQUEST *request, char const *fmt,
2217 RADIUS_ESCAPE_STRING escape, void *escape_ctx)
2223 * Give better errors than the old code.
2225 len = xlat_tokenize_request(request, fmt, &node);
2230 *out = talloc_zero_array(request, char, 1);
2236 if (*out) *out[0] = '\0';
2240 len = xlat_expand_struct(out, outlen, request, node, escape, escape_ctx);
2243 RDEBUG2("EXPAND %s", fmt);
2244 RDEBUG2(" --> %s", *out);
2250 * Try to convert an xlat to a tmpl for efficiency
2252 value_pair_tmpl_t *radius_xlat2tmpl(TALLOC_CTX *ctx, xlat_exp_t *node)
2254 value_pair_tmpl_t *vpt;
2256 if (node->next || (node->type != XLAT_ATTRIBUTE)) return NULL;
2259 * @todo it should be possible to emulate the concat and count operations in the
2262 if ((node->attr.tmpl_num == NUM_COUNT) || (node->attr.tmpl_num == NUM_ALL)) return NULL;
2264 vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, node->fmt, -1);
2265 if (!vpt) return NULL;
2266 vpt->tmpl_request = node->attr.tmpl_request;
2267 vpt->tmpl_list = node->attr.tmpl_list;
2268 vpt->tmpl_da = node->attr.tmpl_da;
2269 vpt->tmpl_num = node->attr.tmpl_num;
2270 vpt->tmpl_tag = node->attr.tmpl_tag;
2277 ssize_t radius_xlat(char *out, size_t outlen, REQUEST *request, char const *fmt, RADIUS_ESCAPE_STRING escape, void *ctx)
2279 return xlat_expand(&out, outlen, request, fmt, escape, ctx);
2282 ssize_t radius_axlat(char **out, REQUEST *request, char const *fmt, RADIUS_ESCAPE_STRING escape, void *ctx)
2284 return xlat_expand(out, 0, request, fmt, escape, ctx);
2287 ssize_t radius_axlat_struct(char **out, REQUEST *request, xlat_exp_t const *xlat, RADIUS_ESCAPE_STRING escape, void *ctx)
2289 return xlat_expand_struct(out, 0, request, xlat, escape, ctx);