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>
37 typedef struct xlat_t {
38 char name[MAX_STRING_LEN]; //!< Name of the xlat expansion.
39 int length; //!< Length of name.
40 void *instance; //!< Module instance passed to xlat and escape functions.
41 RAD_XLAT_FUNC func; //!< xlat function.
42 RADIUS_ESCAPE_STRING escape; //!< Escape function to apply to dynamic input to func.
43 int internal; //!< If true, cannot be redefined.
47 XLAT_LITERAL, //!< Literal string
48 XLAT_PERCENT, //!< Literal string with %v
49 XLAT_MODULE, //!< xlat module
50 XLAT_VIRTUAL, //!< virtual attribute
51 XLAT_ATTRIBUTE, //!< xlat attribute
53 XLAT_REGEX, //!< regex reference
55 XLAT_ALTERNATE //!< xlat conditional syntax :-
59 char const *fmt; //!< The format string.
60 size_t len; //!< Length of the format string.
62 DICT_ATTR const *da; //!< the name of the dictionary attribute
63 int num; //!< attribute number
64 int tag; //!< attribute tag
65 pair_lists_t list; //!< list of which attribute
66 request_refs_t ref; //!< outer / this / ...
68 xlat_state_t type; //!< type of this expansion
69 xlat_exp_t *next; //!< Next in the list.
71 xlat_exp_t *child; //!< Nested expansion.
72 xlat_exp_t *alternate; //!< Alternative expansion if this one expanded to a zero length string.
74 xlat_t const *xlat; //!< The xlat expansion to expand format with.
77 typedef struct xlat_out {
78 char const *out; //!< Output data.
79 size_t len; //!< Length of the output string.
82 static rbtree_t *xlat_root = NULL;
85 static char const * const xlat_foreach_names[] = {"Foreach-Variable-0",
98 #if REQUEST_MAX_REGEX > 8
99 #error Please fix the following line
101 static int xlat_inst[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; /* up to 8 for regex */
105 /** Convert the value on a VALUE_PAIR to string
108 static int valuepair2str(char * out,int outlen,VALUE_PAIR * pair, int type)
111 vp_prints_value(out, outlen, pair, -1);
116 case PW_TYPE_STRING :
117 strlcpy(out,"_",outlen);
119 case PW_TYPE_INTEGER64:
121 case PW_TYPE_INTEGER:
122 strlcpy(out,"0",outlen);
124 case PW_TYPE_IPADDR :
125 strlcpy(out,"?.?.?.?",outlen);
127 case PW_TYPE_IPV6ADDR :
128 strlcpy(out,":?:",outlen);
131 strlcpy(out,"0",outlen);
134 strlcpy(out,"unknown_type",outlen);
140 /** Print length of its RHS.
143 static ssize_t xlat_strlen(UNUSED void *instance, UNUSED REQUEST *request,
144 char const *fmt, char *out, size_t outlen)
146 snprintf(out, outlen, "%u", (unsigned int) strlen(fmt));
150 /** Print data as integer, not as VALUE.
153 static ssize_t xlat_integer(UNUSED void *instance, REQUEST *request,
154 char const *fmt, char *out, size_t outlen)
160 while (isspace((int) *fmt)) fmt++;
162 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
167 switch (vp->da->type) {
170 if (vp->length > 8) {
174 memcpy(&integer, &(vp->vp_octets), vp->length);
176 return snprintf(out, outlen, "%" PRIu64, ntohll(integer));
178 case PW_TYPE_INTEGER64:
179 return snprintf(out, outlen, "%" PRIu64, vp->vp_integer64);
182 case PW_TYPE_INTEGER:
186 return snprintf(out, outlen, "%u", vp->vp_integer);
191 REDEBUG("Type \"%s\" cannot be converted to integer",
192 fr_int2str(dict_attr_types, vp->da->type, PW_TYPE_INVALID));
198 /** Print data as hex, not as VALUE.
201 static ssize_t xlat_hex(UNUSED void *instance, REQUEST *request,
202 char const *fmt, char *out, size_t outlen)
206 uint8_t buffer[MAX_STRING_LEN];
210 while (isspace((int) *fmt)) fmt++;
212 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
217 ret = rad_vp2data(vp, buffer, sizeof(buffer));
221 * Don't truncate the data.
223 if ((ret < 0 ) || (outlen < (len * 2))) {
228 for (i = 0; i < len; i++) {
229 snprintf(out + 2*i, 3, "%02x", buffer[i]);
235 /** Print data as base64, not as VALUE
238 static ssize_t xlat_base64(UNUSED void *instance, REQUEST *request,
239 char const *fmt, char *out, size_t outlen)
242 uint8_t buffer[MAX_STRING_LEN];
245 while (isspace((int) *fmt)) fmt++;
247 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
252 ret = rad_vp2data(vp, buffer, sizeof(buffer));
258 return fr_base64_encode(buffer, (size_t) ret, out, outlen);
261 /** Print out attribute info
263 * Prints out all instances of a current attribute, or all attributes in a list.
265 * At higher debugging levels, also prints out alternative decodings of the same
266 * value. This is helpful to determine types for unknown attributes of long
267 * passed vendors, or just crazy/broken NAS.
269 * It's also useful for exposing issues in the packet decoding functions, as in
270 * some cases they get fed random garbage data.
272 * This expands to a zero length string.
274 static ssize_t xlat_debug_attr(UNUSED void *instance, REQUEST *request, char const *fmt,
275 char *out, UNUSED size_t outlen)
277 VALUE_PAIR *vp, **vps;
279 value_pair_tmpl_t vpt;
283 if (!RDEBUG_ENABLED2) {
288 while (isspace((int) *fmt)) fmt++;
290 if (*fmt == '&') fmt++;
292 if (radius_parse_attr(fmt, &vpt, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
297 if (radius_request(¤t, vpt.request) < 0) return -2;
299 vps = radius_list(current, vpt.list);
304 RIDEBUG("Attributes matching \"%s\"", fmt);
305 vp = paircursor(&cursor, vps);
308 vp = pairfindnext(&cursor, vpt.da->attr, vpt.da->vendor, TAG_ANY);
311 DICT_ATTR *dac = NULL;
314 vp_prints_value(buffer, sizeof(buffer), vp, '\'');
316 if (vp->da->flags.has_tag) {
317 RIDEBUG2("\t%s:%s:%i %s %s",
318 fr_int2str(pair_lists, vpt.list, "<INVALID>"),
321 fr_int2str(fr_tokens, vp->op, "<INVALID>"),
324 RIDEBUG2("\t%s:%s %s %s",
325 fr_int2str(pair_lists, vpt.list, "<INVALID>"),
327 fr_int2str(fr_tokens, vp->op, "<INVALID>"),
331 if (!RDEBUG_ENABLED3) {
335 if (vp->da->vendor) {
336 dv = dict_vendorbyvalue(vp->da->vendor);
337 RDEBUG3("\t\tvendor : %i (%s)", vp->da->vendor, dv ? dv->name : "unknown");
339 RDEBUG3("\t\ttype : %s", fr_int2str(dict_attr_types, vp->da->type, "<INVALID>"));
340 RDEBUG3("\t\tlength : %zu", vp->length);
342 dac = talloc_memdup(request, vp->da, sizeof(DICT_ATTR));
346 dac->flags.vp_free = 0;
353 vp = pairfindnext(&cursor, vpt.da->attr, vpt.da->vendor, TAG_ANY);
355 vp = pairnext(&cursor);
363 /** Prints the current module processing the request
366 static ssize_t xlat_module(UNUSED void *instance, REQUEST *request,
367 UNUSED char const *fmt, char *out, size_t outlen)
369 strlcpy(out, request->module, outlen);
375 /** Implements the Foreach-Variable-X
379 static ssize_t xlat_foreach(void *instance, REQUEST *request,
380 UNUSED char const *fmt, char *out, size_t outlen)
385 * See modcall, "FOREACH" for how this works.
387 pvp = (VALUE_PAIR **) request_data_reference(request, radius_get_vp, *(int*) instance);
393 return valuepair2str(out, outlen, (*pvp), (*pvp)->da->type);
397 /** Print data as string, if possible.
399 * If attribute "Foo" is defined as "octets" it will normally
400 * be printed as 0x0a0a0a. The xlat "%{string:Foo}" will instead
403 static ssize_t xlat_string(UNUSED void *instance, REQUEST *request,
404 char const *fmt, char *out, size_t outlen)
409 while (isspace((int) *fmt)) fmt++;
417 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) goto nothing;
419 if (vp->da->type != PW_TYPE_OCTETS) goto nothing;
421 len = fr_print_string(vp->vp_strvalue, vp->length, out, outlen);
427 /** xlat expand string attribute value
430 static ssize_t xlat_xlat(UNUSED void *instance, REQUEST *request,
431 char const *fmt, char *out, size_t outlen)
435 while (isspace((int) *fmt)) fmt++;
443 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) goto nothing;
445 return radius_xlat(out, outlen, request, vp->vp_strvalue, NULL, NULL);
448 /** Dynamically change the debugging level for the current request
452 static ssize_t xlat_debug(UNUSED void *instance, REQUEST *request,
453 char const *fmt, char *out, size_t outlen)
458 * Expand to previous (or current) level
460 snprintf(out, outlen, "%d", request->options & RAD_REQUEST_OPTION_DEBUG4);
463 * Assume we just want to get the current value and NOT set it to 0
470 request->options = RAD_REQUEST_OPTION_NONE;
471 request->radlog = NULL;
473 if (level > 4) level = 4;
475 request->options = level;
476 request->radlog = radlog_request;
484 * Compare two xlat_t structs, based ONLY on the module name.
486 static int xlat_cmp(void const *one, void const *two)
488 xlat_t const *a = one;
489 xlat_t const *b = two;
491 if (a->length != b->length) {
492 return a->length - b->length;
495 return memcmp(a->name, b->name, a->length);
500 * find the appropriate registered xlat function.
502 static xlat_t *xlat_find(char const *name)
506 strlcpy(my_xlat.name, name, sizeof(my_xlat.name));
507 my_xlat.length = strlen(my_xlat.name);
509 return rbtree_finddata(xlat_root, &my_xlat);
513 /** Register an xlat function.
515 * @param[in] name xlat name.
516 * @param[in] func xlat function to be called.
517 * @param[in] escape function to sanitize any sub expansions passed to the xlat function.
518 * @param[in] instance of module that's registering the xlat function.
519 * @return 0 on success, -1 on failure
521 int xlat_register(char const *name, RAD_XLAT_FUNC func, RADIUS_ESCAPE_STRING escape, void *instance)
526 if (!name || !*name) {
527 DEBUG("xlat_register: Invalid xlat name");
532 * First time around, build up the tree...
534 * FIXME: This code should be hoisted out of this function,
535 * and into a global "initialization". But it isn't critical...
542 xlat_root = rbtree_create(xlat_cmp, free, 0);
544 DEBUG("xlat_register: Failed to create tree.");
549 for (i = 0; xlat_foreach_names[i] != NULL; i++) {
550 xlat_register(xlat_foreach_names[i],
551 xlat_foreach, NULL, &xlat_inst[i]);
552 c = xlat_find(xlat_foreach_names[i]);
553 rad_assert(c != NULL);
558 #define XLAT_REGISTER(_x) xlat_register(STRINGIFY(_x), xlat_ ## _x, NULL, NULL); \
559 c = xlat_find(STRINGIFY(_x)); \
560 rad_assert(c != NULL); \
563 XLAT_REGISTER(integer);
564 XLAT_REGISTER(strlen);
566 XLAT_REGISTER(base64);
567 XLAT_REGISTER(string);
569 XLAT_REGISTER(module);
570 XLAT_REGISTER(debug_attr);
572 xlat_register("debug", xlat_debug, NULL, &xlat_inst[0]);
573 c = xlat_find("debug");
574 rad_assert(c != NULL);
579 * If it already exists, replace the instance.
581 strlcpy(my_xlat.name, name, sizeof(my_xlat.name));
582 my_xlat.length = strlen(my_xlat.name);
583 c = rbtree_finddata(xlat_root, &my_xlat);
586 DEBUG("xlat_register: Cannot re-define internal xlat");
592 c->instance = instance;
597 * Doesn't exist. Create it.
599 c = rad_malloc(sizeof(*c));
600 memset(c, 0, sizeof(*c));
604 strlcpy(c->name, name, sizeof(c->name));
605 c->length = strlen(c->name);
606 c->instance = instance;
608 rbtree_insert(xlat_root, c);
613 /** Unregister an xlat function
615 * We can only have one function to call per name, so the passing of "func"
616 * here is extraneous.
618 * @param[in] name xlat to unregister.
619 * @param[in] func unused.
620 * @param[in] instance data.
622 void xlat_unregister(char const *name, UNUSED RAD_XLAT_FUNC func, void *instance)
629 strlcpy(my_xlat.name, name, sizeof(my_xlat.name));
630 my_xlat.length = strlen(my_xlat.name);
632 c = rbtree_finddata(xlat_root, &my_xlat);
635 if (c->instance != instance) return;
637 rbtree_deletebydata(xlat_root, c);
640 /** De-register all xlat functions, used mainly for debugging.
645 rbtree_free(xlat_root);
649 #define XLAT_DEBUG(fmt, ...) printf(fmt, ## __VA_ARGS__);printf("\n")
654 #define XLAT_DEBUG DEBUG3
656 #define XLAT_DEBUG(...)
660 static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
662 static ssize_t xlat_tokenize_literal(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
663 int brace, char const **error);
664 static size_t xlat_process(char **out, REQUEST *request, xlat_exp_t const * const head,
665 RADIUS_ESCAPE_STRING escape, void *escape_ctx);
667 static ssize_t xlat_tokenize_alternation(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
674 rad_assert(fmt[0] == '%');
675 rad_assert(fmt[1] == '{');
676 rad_assert(fmt[2] == '%');
677 rad_assert(fmt[3] == '{');
679 XLAT_DEBUG("ALTERNATE: %s", fmt);
681 node = talloc_zero(ctx, xlat_exp_t);
682 node->type = XLAT_ALTERNATE;
685 slen = xlat_tokenize_expansion(node, p, &node->child, error);
688 return slen - (p - fmt);
694 *error = "Expected ':' after first expansion";
701 *error = "Expected '-' after ':'";
707 * Allow the RHS to be empty as a special case.
711 * Hack up an empty string.
713 node->alternate = talloc_zero(node, xlat_exp_t);
714 node->alternate->type = XLAT_LITERAL;
715 node->alternate->fmt = talloc_strdup(node->alternate, "");
719 slen = xlat_tokenize_literal(node, p, &node->alternate, true, error);
722 return slen - (p - fmt);
725 if (!node->alternate) {
727 *error = "Empty expansion is invalid";
737 static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
742 char const *attrname;
745 rad_assert(fmt[0] == '%');
746 rad_assert(fmt[1] == '{');
751 if ((fmt[2] == '%') && (fmt[3] == '{')) {
752 return xlat_tokenize_alternation(ctx, fmt, head, error);
755 XLAT_DEBUG("EXPANSION: %s", fmt);
756 node = talloc_zero(ctx, xlat_exp_t);
757 attrname = node->fmt = fmt + 2;
762 * Handle regex's specially.
764 if (isdigit((int) fmt[2]) && (fmt[3] == '}')) {
767 *error = "Invalid regex reference";
771 XLAT_DEBUG("REGEX: %s", fmt);
773 node->num = fmt[2] - '0'; /* ASCII */
775 node->type = XLAT_REGEX;
779 #endif /* HAVE_REGEX_H */
784 * %{Tunnel-Password:1}
785 * %{Tunnel-Password:1[#]}
786 * %{request:Attr-Name}
787 * %{request:Tunnel-Password:1}
788 * %{request:Tunnel-Password:1[#]}
792 for (p = fmt + 2; *p != '\0'; p++) {
793 if (*p == ':') break;
795 if (isspace((int) *p)) break;
797 if (*p == '[') break;
799 if (*p == '}') break;
802 if (*p != ':') p = NULL;
805 * Might be a module name reference.
813 node->xlat = xlat_find(node->fmt);
815 node->type = XLAT_MODULE;
817 XLAT_DEBUG("MOD: %s --> %s", node->fmt, p);
818 slen = xlat_tokenize_literal(node, p + 1, &node->child, true, error);
821 return slen - (p - fmt);
826 rad_assert(node->next == NULL);
831 * Modules can have '}' in their RHS, so we
832 * didn't check for that until now.
834 * As of now, node->fmt MUST be a reference to an
835 * attribute, however complicated. So it MUST have a closing brace.
837 brace = strchr(p + 1, '}');
838 if (!brace) goto no_brace;
844 * %{Tunnel-Password:1}
845 * %{request:Tunnel-Password:1}
847 * <sigh> The syntax is fairly poor.
849 XLAT_DEBUG("Looking for list in '%s'", attrname);
852 * Not a module. Has to be an attribute
855 * As of v3, we've removed %{request: ..>} as
856 * internally registered xlats.
859 node->ref = radius_request_name(&attrname, REQUEST_CURRENT);
860 rad_assert(node->ref != REQUEST_UNKNOWN);
862 node->list = radius_list_name(&attrname, PAIR_LIST_REQUEST);
863 if (node->list == PAIR_LIST_UNKNOWN) {
865 *error = "Unknown module";
870 * Check for a trailing tag.
872 p = strchr(attrname, ':');
876 brace = strchr(attrname, '}');
880 *error = "No matching closing brace";
881 return -1; /* second character of format string */
885 node->ref = REQUEST_CURRENT;
886 node->list = PAIR_LIST_REQUEST;
891 XLAT_DEBUG("Looking for attribute name in %s", attrname);
894 * Allow for an array reference. They come AFTER the
895 * tag, if the tag exists. Otherwise, they come after
896 * the attribute name.
899 q = strchr(p + 1, '[');
901 q = strchr(attrname, '[');
903 if (q) *(q++) = '\0';
907 *error = "Empty expression is invalid";
908 return -(attrname - fmt);
912 * It's either an attribute name, or a Tunnel-Password:TAG
913 * with the ':' already set to NULL.
915 node->da = dict_attrbyname(attrname);
918 * Foreach. Maybe other stuff, too.
920 node->xlat = xlat_find(attrname);
922 node->type = XLAT_VIRTUAL;
923 node->fmt = attrname;
925 XLAT_DEBUG("VIRTUAL: %s", node->fmt);
927 rad_assert(node->next == NULL);
933 *error = "Unknown attribute";
934 return -(attrname - fmt);
944 if (!node->da->flags.has_tag) {
946 *error = "Attribute cannot have a tag";
950 tag = strtoul(p + 1, &end, 10);
953 if (tag == ULONG_MAX) {
955 *error = "Invalid tag value";
964 *error = "Unexpected text after tag";
974 * Check for array reference
985 } else if (*p == '*') {
989 } else if (isdigit((int) *p)) {
990 num = strtoul(p, &end, 10);
991 if ((num == ULONG_MAX) || (num > 65535)) {
993 *error = "Invalid number";
1002 *error = "Invalid array reference";
1008 *error = "Expected ']'";
1015 *error = "Unexpected text after array reference";
1020 rad_assert(!p || (p == brace));
1022 node->type = XLAT_ATTRIBUTE;
1026 rad_assert(node->next == NULL);
1031 static ssize_t xlat_tokenize_literal(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
1032 int brace, char const **error)
1037 if (!*fmt) return 0;
1039 XLAT_DEBUG("LITERAL: %s", fmt);
1041 node = talloc_zero(ctx, xlat_exp_t);
1044 node->type = XLAT_LITERAL;
1051 * Convert \n to it's literal representation.
1053 if (p[0] == '\\') switch (p[1]) {
1068 if (!p[0] || !p[1]) {
1070 *error = "Hex expansion requires two hex digits";
1074 if (!fr_hex2bin((uint8_t *) q, p, 2)) {
1076 *error = "Invalid hex characters";
1081 * Don't let people shoot themselves in the foot.
1082 * \x00 is forbidden.
1086 *error = "Cannot add zero byte to printable string";
1103 * Process the expansion.
1105 if ((p[0] == '%') && (p[1] == '{')) {
1108 XLAT_DEBUG("LITERAL: %s --> %s", node->fmt, p);
1109 slen = xlat_tokenize_expansion(node, p, &node->next, error);
1112 return slen - (p - fmt);
1114 *p = '\0'; /* end the literal */
1117 rad_assert(node->next != NULL);
1120 * Short-circuit the recursive call.
1121 * This saves another function call and
1122 * memory allocation.
1127 * "foo %{User-Name} bar"
1129 * EXPANSION User-Name
1132 slen = xlat_tokenize_literal(node->next, p, &(node->next->next), brace, error);
1133 rad_assert(slen != 0);
1136 return slen - (p - fmt);
1140 break; /* stop processing the string */
1144 * Check for valid single-character expansions.
1150 if (!p[1] || !strchr("%dlmtDGHISTY", p[1])) {
1152 *error = "Invalid variable expansion";
1157 XLAT_DEBUG("PERCENT: %s --> %c", node->fmt, p[1]);
1158 next = talloc_zero(node, xlat_exp_t);
1161 next->type = XLAT_PERCENT;
1172 slen = xlat_tokenize_literal(node->next, p, &(node->next->next), brace, error);
1173 rad_assert(slen != 0);
1176 return slen - (p - fmt);
1180 break; /* stop processing the string */
1184 * If required, eat the brace.
1186 if (brace && (*p == '}')) {
1197 * Squash zero-width literals
1199 if (node->len > 0) {
1202 *head = talloc_steal(ctx, node->next);
1210 static char const xlat_tabs[] = " ";
1212 static void xlat_tokenize_debug(xlat_exp_t const *node, int lvl)
1214 rad_assert(node != NULL);
1216 if (lvl >= (int) sizeof(xlat_tabs)) lvl = sizeof(xlat_tabs);
1219 switch (node->type) {
1221 DEBUG("%.*sliteral: '%s'", lvl, xlat_tabs, node->fmt);
1225 DEBUG("%.*sliteral (with %%): '%c'", lvl, xlat_tabs, node->fmt[0]);
1228 case XLAT_ATTRIBUTE:
1229 rad_assert(node->da != NULL);
1230 DEBUG("%.*sattribute: %s", lvl, xlat_tabs, node->da->name);
1231 rad_assert(node->child == NULL);
1232 if ((node->tag != 0) || (node->num != 0)) {
1233 DEBUG("%.*s{", lvl, xlat_tabs);
1235 DEBUG("%.*sref %d", lvl + 1, xlat_tabs, node->ref);
1236 DEBUG("%.*slist %d", lvl + 1, xlat_tabs, node->list);
1238 if (node->tag) DEBUG("%.*stag %d", lvl + 1, xlat_tabs, node->tag);
1240 if (node->num == 65536) {
1241 DEBUG("%.*s[#]", lvl + 1, xlat_tabs);
1242 } else if (node->num == 65537) {
1243 DEBUG("%.*s[*]", lvl + 1, xlat_tabs);
1245 DEBUG("%.*s[%d]", lvl + 1, xlat_tabs, node->num);
1249 DEBUG("%.*s}", lvl, xlat_tabs);
1254 rad_assert(node->fmt != NULL);
1255 DEBUG("%.*svirtual: %s", lvl, xlat_tabs, node->fmt);
1259 rad_assert(node->xlat != NULL);
1260 DEBUG("%.*sxlat: %s", lvl, xlat_tabs, node->xlat->name);
1262 DEBUG("%.*s{", lvl, xlat_tabs);
1263 xlat_tokenize_debug(node->child, lvl + 1);
1264 DEBUG("%.*s}", lvl, xlat_tabs);
1270 DEBUG("%.*sregex-var: %d", lvl, xlat_tabs, node->num);
1274 case XLAT_ALTERNATE:
1275 DEBUG("%.*sif {", lvl, xlat_tabs);
1276 xlat_tokenize_debug(node->child, lvl + 1);
1277 DEBUG("%.*s}", lvl, xlat_tabs);
1278 DEBUG("%.*selse {", lvl, xlat_tabs);
1279 xlat_tokenize_debug(node->alternate, lvl + 1);
1280 DEBUG("%.*s}", lvl, xlat_tabs);
1287 size_t xlat_sprint(char *buffer, size_t bufsize, xlat_exp_t const *node)
1298 end = buffer + bufsize;
1301 switch (node->type) {
1303 strlcpy(p, node->fmt, end - p);
1309 p[1] = node->fmt[0];
1313 case XLAT_ATTRIBUTE:
1317 if (node->ref != REQUEST_CURRENT) {
1318 strlcpy(p, fr_int2str(request_refs, node->ref, "??"), end - p);
1323 if ((node->ref != REQUEST_CURRENT) ||
1324 (node->list != PAIR_LIST_REQUEST)) {
1325 strlcpy(p, fr_int2str(pair_lists, node->list, "??"), end - p);
1330 strlcpy(p, node->da->name, end - p);
1333 if (node->tag != TAG_ANY) {
1335 snprintf(p, end - p, "%u", node->tag);
1339 if (node->num != 0) {
1342 if (node->num == 65536) {
1345 } else if (node->num == 65537) {
1349 snprintf(p, end - p, "%u", node->num);
1358 snprintf(p, end - p, "%%{%u}", node->num);
1365 strlcpy(p, node->fmt, end - p);
1373 strlcpy(p, node->xlat->name, end - p);
1376 rad_assert(node->child != NULL);
1377 len = xlat_sprint(p, end - p, node->child);
1382 case XLAT_ALTERNATE:
1386 len = xlat_sprint(p, end - p, node->child);
1392 len = xlat_sprint(p, end - p, node->alternate);
1400 if (p == end) break;
1411 static char const xlat_spaces[] = " ";
1414 ssize_t xlat_tokenize(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
1417 return xlat_tokenize_literal(ctx, fmt, head, false, error);
1421 /** Tokenize an xlat expansion
1423 * @param[in] request the input request. Memory will be attached here.
1424 * @param[in] fmt the format string to expand
1425 * @param[out] head the head of the xlat list / tree structure.
1427 static ssize_t xlat_tokenize_request(REQUEST *request, char const *fmt, xlat_exp_t **head)
1436 * Copy the original format string to a buffer so that
1437 * the later functions can mangle it in-place, which is
1440 tokens = talloc_strdup(request, fmt);
1441 if (!tokens) return -1;
1443 slen = xlat_tokenize_literal(request, tokens, head, false, &error);
1445 * Zero length expansion, return a zero length node.
1448 *head = talloc_zero(request, xlat_exp_t);
1452 * Output something like:
1455 * " ^ error was here"
1458 size_t indent = -slen;
1459 talloc_free(tokens);
1461 rad_assert(error != NULL);
1462 if (indent < sizeof(xlat_spaces)) {
1464 REDEBUG("%.*s^ %s", (int) -slen, xlat_spaces, error);
1469 if (*head && (debug_flag > 2)) {
1471 DEBUG("Parsed xlat tree:");
1472 xlat_tokenize_debug(*head, 0);
1476 * All of the nodes point to offsets in the "tokens"
1477 * string. Let's ensure that free'ing head will free
1480 (void) talloc_steal(*head, tokens);
1486 static char *xlat_getvp(TALLOC_CTX *ctx, REQUEST *request, pair_lists_t list, DICT_ATTR const *da, int8_t tag,
1487 int num, bool return_null)
1489 VALUE_PAIR *vp, *vps = NULL;
1490 RADIUS_PACKET *packet = NULL;
1495 * Arg. Too much abstraction is annoying.
1499 if (return_null) return NULL;
1500 return vp_aprinttype(ctx, da->type);
1502 case PAIR_LIST_CONTROL:
1503 vps = request->config_items;
1506 case PAIR_LIST_REQUEST:
1507 packet = request->packet;
1508 if (packet) vps = packet->vps;
1511 case PAIR_LIST_REPLY:
1512 packet = request->reply;
1513 if (packet) vps = packet->vps;
1517 case PAIR_LIST_PROXY_REQUEST:
1518 packet = request->proxy;
1519 if (packet) vps = packet->vps;
1522 case PAIR_LIST_PROXY_REPLY:
1523 packet = request->proxy_reply;
1524 if (packet) vps = packet->vps;
1531 if (request->coa) packet = request->coa->packet;
1532 if (packet) vps = packet->vps;
1535 case PAIR_LIST_COA_REPLY:
1536 case PAIR_LIST_DM_REPLY:
1537 if (request->coa) packet = request->coa->reply;
1538 if (packet) vps = packet->vps;
1545 * Now that we have the list, etc. handled,
1546 * find the VP and print it.
1548 if ((da->vendor != 0) || (da->attr < 256) || (list == PAIR_LIST_CONTROL)) {
1550 vp = pairfind(vps, da->attr, da->vendor, tag);
1558 * Some non-packet expansions
1562 break; /* ignore them */
1564 case PW_CLIENT_SHORTNAME:
1565 if (request->client && request->client->shortname) {
1566 return talloc_strdup(ctx, request->client->shortname);
1568 return talloc_strdup(ctx, "<UNKNOWN-CLIENT>");
1570 case PW_REQUEST_PROCESSING_STAGE:
1571 if (request->component) {
1572 return talloc_strdup(ctx, request->component);
1574 return talloc_strdup(ctx, "server_core");
1576 case PW_VIRTUAL_SERVER:
1577 if (!request->server) return NULL;
1578 return talloc_strdup(ctx, request->server);
1580 case PW_MODULE_RETURN_CODE:
1581 return talloc_asprintf(ctx, "%d", request->simul_max); /* hack */
1585 * All of the attributes must now refer to a packet. If
1586 * there's no packet, we can't print any attribute
1590 if (return_null) return NULL;
1591 return vp_aprinttype(ctx, da->type);
1594 memset(&myvp, 0, sizeof(myvp));
1602 case PW_PACKET_TYPE:
1603 dv = dict_valbyattr(PW_PACKET_TYPE, 0, packet->code);
1604 if (dv) return talloc_strdup(ctx, dv->name);
1605 return talloc_asprintf(ctx, "%d", packet->code);
1607 case PW_RESPONSE_PACKET_TYPE:
1612 if (request->proxy_reply && (!request->reply || !request->reply->code)) {
1613 code = request->proxy_reply->code;
1616 if (request->reply) {
1617 code = request->reply->code;
1620 return talloc_strdup(ctx, fr_packet_codes[code]);
1623 case PW_PACKET_AUTHENTICATION_VECTOR:
1624 myvp.length = sizeof(packet->vector);
1625 memcpy(&myvp.vp_octets, packet->vector, sizeof(packet->vector));
1629 case PW_CLIENT_IP_ADDRESS:
1630 case PW_PACKET_SRC_IP_ADDRESS:
1631 if (packet->src_ipaddr.af == AF_INET) {
1632 myvp.vp_ipaddr = packet->src_ipaddr.ipaddr.ip4addr.s_addr;
1637 case PW_PACKET_DST_IP_ADDRESS:
1638 if (packet->dst_ipaddr.af == AF_INET) {
1639 myvp.vp_ipaddr = packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
1644 case PW_PACKET_SRC_IPV6_ADDRESS:
1645 if (packet->src_ipaddr.af == AF_INET6) {
1646 memcpy(&myvp.vp_ipv6addr,
1647 &packet->src_ipaddr.ipaddr.ip6addr,
1648 sizeof(packet->src_ipaddr.ipaddr.ip6addr));
1653 case PW_PACKET_DST_IPV6_ADDRESS:
1654 if (packet->dst_ipaddr.af == AF_INET6) {
1655 memcpy(&myvp.vp_ipv6addr,
1656 &packet->dst_ipaddr.ipaddr.ip6addr,
1657 sizeof(packet->dst_ipaddr.ipaddr.ip6addr));
1662 case PW_PACKET_SRC_PORT:
1663 myvp.vp_integer = packet->src_port;
1667 case PW_PACKET_DST_PORT:
1668 myvp.vp_integer = packet->dst_port;
1675 * Hack up the virtual attributes.
1677 if (num && (vp == &myvp)) {
1681 * [*] means only one.
1683 if (num == 65537) num = 0;
1686 * [n] means NULL, as there's only one.
1688 if ((num > 0) && (num < 65536)) {
1692 p = vp_aprint(ctx, vp);
1693 rad_assert(p != NULL);
1696 * Get the length of it.
1699 q = talloc_asprintf(ctx, "%d", (int) strlen(p));
1708 * We want the N'th VP.
1715 * Return a count of the VPs.
1718 paircursor(&cursor, &vp);
1719 while (pairfindnext(&cursor, da->attr, da->vendor, tag) != NULL) {
1723 return talloc_asprintf(ctx, "%d", count);
1727 * Ugly, but working.
1732 vp = paircursor(&cursor, &vp);
1733 vp = pairfindnext(&cursor, da->attr, da->vendor, tag);
1734 if (!vp) return NULL;
1735 p = vp_aprint(ctx, vp);
1736 while ((vp = pairfindnext(&cursor, da->attr, da->vendor, tag)) != NULL) {
1737 q = vp_aprint(ctx, vp);
1738 p = talloc_strdup_append(p, ",");
1739 p = talloc_strdup_append(p, q);
1745 paircursor(&cursor, &vp);
1746 while (pairfindnext(&cursor, da->attr, da->vendor, tag) != NULL) {
1755 if (return_null) return NULL;
1756 return vp_aprinttype(ctx, da->type);
1759 return vp_aprint(ctx, vp);
1762 static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * const node,
1763 RADIUS_ESCAPE_STRING escape, void *escape_ctx, int lvl)
1766 char *str = NULL, *child;
1769 XLAT_DEBUG("%.*sxlat aprint %d", lvl, xlat_spaces, node->type);
1771 switch (node->type) {
1773 * Don't escape this.
1776 XLAT_DEBUG("xlat_aprint LITERAL");
1777 return talloc_strdup(ctx, node->fmt);
1780 * Do a one-character expansion.
1786 size_t freespace = 256;
1790 XLAT_DEBUG("xlat_aprint PERCENT");
1792 str = talloc_array(ctx, char, freespace); /* @todo do better allocation */
1795 when = request->timestamp;
1796 if (request->packet) {
1797 when = request->packet->timestamp.tv_sec;
1806 case 'd': /* request day */
1807 (void) localtime_r(&when, &ts);
1808 strftime(str, freespace, "%d", &ts);
1811 case 'l': /* request timestamp */
1812 snprintf(str, freespace, "%lu",
1813 (unsigned long) when);
1816 case 'm': /* request month */
1817 (void) localtime_r(&when, &ts);
1818 strftime(str, freespace, "%m", &ts);
1821 case 't': /* request timestamp */
1822 CTIME_R(&when, str, freespace);
1823 nl = strchr(str, '\n');
1827 case 'D': /* request date */
1828 (void) localtime_r(&when, &ts);
1829 strftime(str, freespace, "%Y%m%d", &ts);
1832 case 'G': /* request minute */
1833 (void) localtime_r(&when, &ts);
1834 strftime(str, freespace, "%M", &ts);
1837 case 'H': /* request hour */
1838 (void) localtime_r(&when, &ts);
1839 strftime(str, freespace, "%H", &ts);
1842 case 'I': /* Request ID */
1843 if (request->packet) {
1844 snprintf(str, freespace, "%i", request->packet->id);
1848 case 'S': /* request timestamp in SQL format*/
1849 (void) localtime_r(&when, &ts);
1850 strftime(str, freespace, "%Y-%m-%d %H:%M:%S", &ts);
1853 case 'T': /* request timestamp */
1854 (void) localtime_r(&when, &ts);
1855 strftime(str, freespace, "%Y-%m-%d-%H.%M.%S.000000", &ts);
1858 case 'Y': /* request year */
1859 (void) localtime_r(&when, &ts);
1860 strftime(str, freespace, "%Y", &ts);
1870 case XLAT_ATTRIBUTE:
1871 XLAT_DEBUG("xlat_aprint ATTRIBUTE");
1873 if (radius_request(&ref, node->ref) < 0) {
1878 * Some attributes are virtual <sigh>
1880 str = xlat_getvp(ctx, ref, node->list, node->da, node->tag, node->num, true);
1882 XLAT_DEBUG("expand attr %s --> '%s'", node->da->name, str);
1887 XLAT_DEBUG("xlat_aprint VIRTUAL");
1888 str = talloc_array(ctx, char, 1024); /* FIXME: have the module call talloc_asprintf */
1889 rcode = node->xlat->func(node->xlat->instance, request, NULL, str, 1024);
1897 XLAT_DEBUG("xlat_aprint MODULE");
1898 if (xlat_process(&child, request, node->child, node->xlat->escape, node->xlat->instance) == 0) {
1902 XLAT_DEBUG("%.*sexpand mod %s --> '%s'", lvl, xlat_spaces, node->fmt, child);
1904 str = talloc_array(ctx, char, 1024); /* FIXME: have the module call talloc_asprintf */
1905 *str = '\0'; /* Be sure the string is NULL terminated, we now only free on error */
1907 rcode = node->xlat->func(node->xlat->instance, request, child, str, 1024);
1917 XLAT_DEBUG("xlat_aprint REGEX");
1918 child = request_data_reference(request, request,
1919 REQUEST_DATA_REGEX | node->num);
1920 if (!child) return NULL;
1922 str = talloc_strdup(ctx, child);
1926 case XLAT_ALTERNATE:
1927 XLAT_DEBUG("xlat_aprint ALTERNATE");
1928 rad_assert(node->child != NULL);
1929 rad_assert(node->alternate != NULL);
1931 str = xlat_aprint(ctx, request, node->child, escape, escape_ctx, lvl);
1934 str = xlat_aprint(ctx, request, node->alternate, escape, escape_ctx, lvl);
1940 * Escape the non-literals we found above.
1942 if (str && escape) {
1945 escaped = talloc_array(ctx, char, 1024); /* FIXME: do something intelligent */
1946 escape(request, escaped, 1024, str, escape_ctx);
1955 static size_t xlat_process(char **out, REQUEST *request, xlat_exp_t const * const head,
1956 RADIUS_ESCAPE_STRING escape, void *escape_ctx)
1960 char **array, *answer;
1961 xlat_exp_t const *node;
1966 * There are no nodes to process, so the result is a zero
1970 *out = talloc_zero_array(request, char, 1);
1975 * Hack for speed. If it's one expansion, just allocate
1976 * that and return, instead of allocating an intermediary
1981 * Pass the MAIN escape function. Recursive
1982 * calls will call node-specific escape
1985 answer = xlat_aprint(request, request, head, escape, escape_ctx, 0);
1987 *out = talloc_zero_array(request, char, 1);
1991 return strlen(answer);
1994 list = 0; /* FIXME: calculate this once */
1995 for (node = head; node != NULL; node = node->next) {
1999 array = talloc_array(request, char *, list);
2000 if (!array) return -1;
2002 for (node = head, i = 0; node != NULL; node = node->next, i++) {
2003 array[i] = xlat_aprint(array, request, node, escape, escape_ctx, 0); /* may be NULL */
2007 for (i = 0; i < list; i++) {
2008 if (array[i]) total += strlen(array[i]); /* FIXME: calculate strlen once */
2013 *out = talloc_zero_array(request, char, 1);
2017 answer = talloc_array(request, char, total + 1);
2020 for (i = 0; i < list; i++) {
2024 len = strlen(array[i]);
2025 memcpy(answer + total, array[i], len);
2029 answer[total] = '\0';
2030 talloc_free(array); /* and child entries */
2037 /** Replace %whatever in a string.
2039 * See 'doc/variables.txt' for more information.
2041 * @param[out] out Where to write pointer to output buffer.
2042 * @param[in] outlen Size of out.
2043 * @param[in] request current request.
2044 * @param[in] fmt string to expand.
2045 * @param[in] escape function to escape final value e.g. SQL quoting.
2046 * @param[in] escape_ctx pointer to pass to escape function.
2047 * @return length of string written @bug should really have -1 for failure
2049 static ssize_t xlat_expand(char **out, size_t outlen, REQUEST *request, char const *fmt,
2050 RADIUS_ESCAPE_STRING escape, void *escape_ctx)
2057 rad_assert(request);
2060 * Give better errors than the old code.
2062 len = xlat_tokenize_request(request, fmt, &node);
2067 *out = talloc_zero_array(request, char, 1);
2073 if (*out) *out[0] = '\0';
2077 len = xlat_process(&buff, request, node, escape, escape_ctx);
2080 if ((len < 0) || !buff) {
2081 rad_assert(buff == NULL);
2082 if (*out) *out[0] = '\0';
2086 RDEBUG2("\texpand: \"%s\" -> '%s'", fmt, buff);
2091 strlcpy(*out, buff, outlen);
2094 return strlen(*out);
2097 ssize_t radius_xlat(char *out, size_t outlen, REQUEST *request, char const *fmt, RADIUS_ESCAPE_STRING escape, void *ctx)
2099 return xlat_expand(&out, outlen, request, fmt, escape, ctx);
2102 ssize_t radius_axlat(char **out, REQUEST *request, char const *fmt, RADIUS_ESCAPE_STRING escape, void *ctx)
2104 return xlat_expand(out, 0, request, fmt, escape, ctx);