char name[MAX_STRING_LEN]; //!< Name of the xlat expansion.
int length; //!< Length of name.
void *instance; //!< Module instance passed to xlat and escape functions.
- RAD_XLAT_FUNC func; //!< xlat function.
- RADIUS_ESCAPE_STRING escape; //!< Escape function to apply to dynamic input to func.
+ xlat_func_t func; //!< xlat function.
+ xlat_escape_t escape; //!< Escape function to apply to dynamic input to func.
bool internal; //!< If true, cannot be redefined.
} xlat_t;
xlat_exp_t *child; //!< Nested expansion.
xlat_exp_t *alternate; //!< Alternative expansion if this one expanded to a zero length string.
- value_pair_tmpl_t attr; //!< An attribute template.
+ vp_tmpl_t attr; //!< An attribute template.
xlat_t const *xlat; //!< The xlat expansion to expand format with.
};
NULL};
#endif
-#if REQUEST_MAX_REGEX > 8
-# error Please fix the following line
-#endif
-static int xlat_inst[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; /* up to 8 for regex */
-char const *radiusd_short_version = RADIUSD_VERSION_STRING;
+static int xlat_inst[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; /* up to 10 for foreach */
/** Print length of its RHS.
*
/** Print the size of the attribute in bytes.
*
*/
-static ssize_t xlat_length(UNUSED void *instance, UNUSED REQUEST *request,
+static ssize_t xlat_length(UNUSED void *instance, REQUEST *request,
char const *fmt, char *out, size_t outlen)
{
VALUE_PAIR *vp;
return 0;
}
- snprintf(out, outlen, "%zu", vp->length);
+ snprintf(out, outlen, "%zu", vp->vp_length);
return strlen(out);
}
switch (vp->da->type) {
case PW_TYPE_OCTETS:
case PW_TYPE_STRING:
- if (vp->length > 8) {
+ if (vp->vp_length > 8) {
break;
}
- if (vp->length > 4) {
- memcpy(&int64, vp->vp_octets, vp->length);
+ if (vp->vp_length > 4) {
+ memcpy(&int64, vp->vp_octets, vp->vp_length);
return snprintf(out, outlen, "%" PRIu64, htonll(int64));
}
- memcpy(&int32, vp->vp_octets, vp->length);
+ memcpy(&int32, vp->vp_octets, vp->vp_length);
return snprintf(out, outlen, "%i", htonl(int32));
case PW_TYPE_INTEGER64:
return snprintf(out, outlen, "%u", htonl((*(uint32_t *)(vp->vp_ipv4prefix + 2))));
case PW_TYPE_INTEGER:
- case PW_TYPE_DATE:
return snprintf(out, outlen, "%u", vp->vp_integer);
+
+ case PW_TYPE_DATE:
+ return snprintf(out, outlen, "%u", vp->vp_date);
+
case PW_TYPE_BYTE:
return snprintf(out, outlen, "%u", (unsigned int) vp->vp_byte);
+
case PW_TYPE_SHORT:
return snprintf(out, outlen, "%u", (unsigned int) vp->vp_short);
* bigendian.
*/
case PW_TYPE_ETHERNET:
- memcpy(&int64, &vp->vp_ether, vp->length);
+ memcpy(&int64, vp->vp_ether, vp->vp_length);
return snprintf(out, outlen, "%" PRIu64, htonll(int64));
case PW_TYPE_SIGNED:
return fr_prints_uint128(out, outlen, ntohlll(*(uint128_t const *) &vp->vp_ipv6addr));
case PW_TYPE_IPV6_PREFIX:
- return fr_prints_uint128(out, outlen, ntohlll(*(uint128_t const *) &(vp->vp_ipv6prefix[2])));
+ return fr_prints_uint128(out, outlen, ntohlll(*(uint128_t const *) &vp->vp_ipv6prefix[2]));
default:
break;
}
REDEBUG("Type '%s' of length %zu cannot be converted to integer",
- fr_int2str(dict_attr_types, vp->da->type, "???"), vp->length);
+ fr_int2str(dict_attr_types, vp->da->type, "???"), vp->vp_length);
*out = '\0';
return -1;
uint8_t const *p;
ssize_t ret;
size_t len;
+ value_data_t dst;
+ uint8_t const *buff = NULL;
while (isspace((int) *fmt)) fmt++;
if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
+ error:
*out = '\0';
return -1;
}
- ret = rad_vp2data(&p, vp);
- if (ret < 0) {
- return ret;
+ /*
+ * The easy case.
+ */
+ if (vp->da->type == PW_TYPE_OCTETS) {
+ p = vp->vp_octets;
+ len = vp->vp_length;
+ /*
+ * Cast the value_data_t of the VP to an octets string and
+ * print that as hex.
+ */
+ } else {
+ ret = value_data_cast(request, &dst, PW_TYPE_OCTETS, NULL, vp->da->type,
+ NULL, &vp->data, vp->vp_length);
+ if (ret < 0) {
+ REDEBUG("%s", fr_strerror());
+ goto error;
+ }
+ len = (size_t) ret;
+ p = buff = dst.octets;
}
- len = (size_t) ret;
+
+ rad_assert(p);
/*
* Don't truncate the data.
*/
- if ((ret < 0 ) || (outlen < (len * 2))) {
- *out = 0;
- return 0;
+ if (outlen < (len * 2)) {
+ rad_const_free(buff);
+ goto error;
}
for (i = 0; i < len; i++) {
snprintf(out + 2*i, 3, "%02x", p[i]);
}
+ rad_const_free(buff);
return len * 2;
}
return snprintf(out, outlen, "%u", vp->tag);
}
+/** Return the vendor of an attribute reference
+ *
+ */
+static ssize_t xlat_vendor(UNUSED void *instance, REQUEST *request,
+ char const *fmt, char *out, size_t outlen)
+{
+ VALUE_PAIR *vp;
+ DICT_VENDOR *vendor;
+
+ while (isspace((int) *fmt)) fmt++;
+
+ if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
+ *out = '\0';
+ return 0;
+ }
+
+ vendor = dict_vendorbyvalue(vp->da->vendor);
+ if (!vendor) {
+ *out = '\0';
+ return 0;
+ }
+ strlcpy(out, vendor->name, outlen);
+
+ return vendor->length;
+}
+
+/** Return the vendor number of an attribute reference
+ *
+ */
+static ssize_t xlat_vendor_num(UNUSED void *instance, REQUEST *request,
+ char const *fmt, char *out, size_t outlen)
+{
+ VALUE_PAIR *vp;
+
+ while (isspace((int) *fmt)) fmt++;
+
+ if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
+ *out = '\0';
+ return 0;
+ }
+
+ return snprintf(out, outlen, "%u", vp->da->vendor);
+}
+
+/** Return the attribute name of an attribute reference
+ *
+ */
+static ssize_t xlat_attr(UNUSED void *instance, REQUEST *request,
+ char const *fmt, char *out, size_t outlen)
+{
+ VALUE_PAIR *vp;
+
+ while (isspace((int) *fmt)) fmt++;
+
+ if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
+ *out = '\0';
+ return 0;
+ }
+ strlcpy(out, vp->da->name, outlen);
+
+ return strlen(vp->da->name);
+}
+
+/** Return the attribute number of an attribute reference
+ *
+ */
+static ssize_t xlat_attr_num(UNUSED void *instance, REQUEST *request,
+ char const *fmt, char *out, size_t outlen)
+{
+ VALUE_PAIR *vp;
+
+ while (isspace((int) *fmt)) fmt++;
+
+ if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
+ *out = '\0';
+ return 0;
+ }
+
+ return snprintf(out, outlen, "%u", vp->da->attr);
+}
+
/** Print out attribute info
*
* Prints out all instances of a current attribute, or all attributes in a list.
* value. This is helpful to determine types for unknown attributes of long
* passed vendors, or just crazy/broken NAS.
*
- * It's also useful for exposing issues in the packet decoding functions, as in
- * some cases they get fed random garbage data.
- *
* This expands to a zero length string.
*/
static ssize_t xlat_debug_attr(UNUSED void *instance, REQUEST *request, char const *fmt,
char *out, UNUSED size_t outlen)
{
- VALUE_PAIR *vp, **vps;
- REQUEST *current;
+ VALUE_PAIR *vp;
vp_cursor_t cursor;
- char buffer[1024];
- value_pair_tmpl_t vpt;
+ vp_tmpl_t vpt;
if (!RDEBUG_ENABLED2) {
*out = '\0';
while (isspace((int) *fmt)) fmt++;
- if (tmpl_from_attr_str(&vpt, fmt, REQUEST_CURRENT, PAIR_LIST_REQUEST) <= 0) {
+ if (tmpl_from_attr_str(&vpt, fmt, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false) <= 0) {
RDEBUG("%s", fr_strerror());
return -1;
}
- current = request;
- if (radius_request(¤t, vpt.tmpl_request) < 0) return -2;
-
- vps = radius_list(current, vpt.tmpl_list);
- if (!vps) {
- return -2;
- }
-
RIDEBUG("Attributes matching \"%s\"", fmt);
- vp = fr_cursor_init(&cursor, vps);
-
- if (vpt.tmpl_da) {
- vp = fr_cursor_next_by_da(&cursor, vpt.tmpl_da, TAG_ANY);
- }
- while (vp) {
- DICT_ATTR *dac = NULL;
- DICT_VENDOR *dv;
- vp_prints_value(buffer, sizeof(buffer), vp, '\'');
+ RINDENT();
+ for (vp = tmpl_cursor_init(NULL, &cursor, request, &vpt);
+ vp;
+ vp = tmpl_cursor_next(&cursor, &vpt)) {
+ FR_NAME_NUMBER const *type;
+ char *value;
- RINDENT();
+ value = vp_aprints_value(vp, vp, '\'');
if (vp->da->flags.has_tag) {
- RIDEBUG2("%s:%s:%i %s %s",
+ RIDEBUG2("&%s:%s:%i %s %s",
fr_int2str(pair_lists, vpt.tmpl_list, "<INVALID>"),
vp->da->name,
vp->tag,
fr_int2str(fr_tokens, vp->op, "<INVALID>"),
- buffer);
+ value);
} else {
- RIDEBUG2("%s:%s %s %s",
+ RIDEBUG2("&%s:%s %s %s",
fr_int2str(pair_lists, vpt.tmpl_list, "<INVALID>"),
vp->da->name,
fr_int2str(fr_tokens, vp->op, "<INVALID>"),
- buffer);
+ value);
}
- REXDENT();
+ talloc_free(value);
- if (!RDEBUG_ENABLED3) goto next_vp;
+ if (!RDEBUG_ENABLED3) continue;
- RINDENT();
- RINDENT();
if (vp->da->vendor) {
+ DICT_VENDOR *dv;
+
dv = dict_vendorbyvalue(vp->da->vendor);
- RDEBUG3("Vendor : %i (%s)", vp->da->vendor, dv ? dv->name : "unknown");
+ RIDEBUG2("Vendor : %i (%s)", vp->da->vendor, dv ? dv->name : "unknown");
}
- RDEBUG3("Type : %s", fr_int2str(dict_attr_types, vp->da->type, "<INVALID>"));
- RDEBUG3("Length : %zu", vp->length);
- REXDENT();
- REXDENT();
+ RIDEBUG2("Type : %s", fr_int2str(dict_attr_types, vp->da->type, "<INVALID>"));
+ RIDEBUG2("Length : %zu", vp->vp_length);
- dac = talloc_memdup(request, vp->da, sizeof(DICT_ATTR));
- if (!dac) return -1;
- talloc_set_type(dac, DICT_ATTR);
+ if (!RDEBUG_ENABLED4) continue;
- next_vp:
- talloc_free(dac);
+ type = dict_attr_types;
+ while (type->name) {
+ int pad;
- if (vpt.tmpl_da) {
- vp = fr_cursor_next_by_da(&cursor, vpt.tmpl_da, TAG_ANY);
- } else {
- vp = fr_cursor_next(&cursor);
+ value_data_t *dst = NULL;
+
+ ssize_t ret;
+
+ if ((PW_TYPE) type->number == vp->da->type) {
+ goto next_type;
+ }
+
+ switch (type->number) {
+ case PW_TYPE_INVALID: /* Not real type */
+ case PW_TYPE_MAX: /* Not real type */
+ case PW_TYPE_EXTENDED: /* Not safe/appropriate */
+ case PW_TYPE_LONG_EXTENDED: /* Not safe/appropriate */
+ case PW_TYPE_TLV: /* Not safe/appropriate */
+ case PW_TYPE_EVS: /* Not safe/appropriate */
+ case PW_TYPE_VSA: /* @fixme We need special behaviour for these */
+ case PW_TYPE_COMBO_IP_ADDR: /* Covered by IPv4 address IPv6 address */
+ case PW_TYPE_COMBO_IP_PREFIX: /* Covered by IPv4 address IPv6 address */
+ case PW_TYPE_TIMEVAL: /* Not a VALUE_PAIR type */
+ goto next_type;
+
+ default:
+ break;
+ }
+
+ dst = talloc_zero(vp, value_data_t);
+ ret = value_data_cast(dst, dst, type->number, NULL, vp->da->type, vp->da,
+ &vp->data, vp->vp_length);
+ if (ret < 0) goto next_type; /* We expect some to fail */
+
+ value = value_data_aprints(dst, type->number, NULL, dst, (size_t)ret, '\'');
+ if (!value) goto next_type;
+
+ if ((pad = (11 - strlen(type->name))) < 0) {
+ pad = 0;
+ }
+
+ RINDENT();
+ RDEBUG2("as %s%*s: %s", type->name, pad, " ", value);
+ REXDENT();
+
+ next_type:
+ talloc_free(dst);
+ type++;
}
}
return 0;
}
+/** Processes fmt as a map string and applies it to the current request
+ *
+ * e.g. "%{map:&User-Name := 'foo'}"
+ *
+ * Allows sets of modifications to be cached and then applied.
+ * Useful for processing generic attributes from LDAP.
+ */
+static ssize_t xlat_map(UNUSED void *instance, REQUEST *request,
+ char const *fmt, char *out, size_t outlen)
+{
+ vp_map_t *map = NULL;
+ int ret;
+
+ if (map_afrom_attr_str(request, &map, fmt,
+ REQUEST_CURRENT, PAIR_LIST_REQUEST,
+ REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
+ REDEBUG("Failed parsing \"%s\" as map: %s", fmt, fr_strerror());
+ return -1;
+ }
+
+ RINDENT();
+ ret = map_to_request(request, map, map_to_vp, NULL);
+ REXDENT();
+ talloc_free(map);
+ if (ret < 0) return strlcpy(out, "0", outlen);
+
+ return strlcpy(out, "1", outlen);
+}
+
/** Prints the current module processing the request
*
*/
return strlen(out);
}
+#if defined(HAVE_REGEX) && defined(HAVE_PCRE)
+static ssize_t xlat_regex(UNUSED void *instance, REQUEST *request,
+ char const *fmt, char *out, size_t outlen)
+{
+ char *p;
+ size_t len;
+
+ if (regex_request_to_sub_named(request, &p, request, fmt) < 0) {
+ *out = '\0';
+ return 0;
+ }
+
+ len = talloc_array_length(p);
+ if (len > outlen) {
+ RDEBUG("Insufficient buffer space to write subcapture value, needed %zu bytes, have %zu bytes",
+ len, outlen);
+ return -1;
+ }
+ strlcpy(out, p, outlen);
+
+ return len - 1; /* - \0 */
+}
+#endif
+
#ifdef WITH_UNLANG
/** Implements the Foreach-Variable-X
*
/*
* See modcall, "FOREACH" for how this works.
*/
- pvp = (VALUE_PAIR **) request_data_reference(request, radius_get_vp, *(int*) instance);
+ pvp = (VALUE_PAIR **) request_data_reference(request, (void *)radius_get_vp, *(int*) instance);
if (!pvp || !*pvp) {
*out = '\0';
return 0;
switch (vp->da->type) {
case PW_TYPE_OCTETS:
- len = fr_print_string((char const *) p, vp->length, out, outlen, '\0');
+ len = fr_prints(out, outlen, (char const *) p, vp->vp_length, '"');
break;
+ /*
+ * Note that "%{string:...}" is NOT binary safe!
+ * It is explicitly used to get rid of embedded zeros.
+ */
case PW_TYPE_STRING:
len = strlcpy(out, vp->vp_strvalue, outlen);
break;
default:
- len = fr_print_string((char const *) p, ret, out, outlen, '\0');
+ len = fr_prints(out, outlen, (char const *) p, ret, '\0');
break;
}
if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) goto nothing;
+ if (vp->da->type != PW_TYPE_STRING) goto nothing;
+
return radius_xlat(out, outlen, request, vp->vp_strvalue, NULL, NULL);
}
/*
* Expand to previous (or current) level
*/
- snprintf(out, outlen, "%d", request->log.lvl & RAD_REQUEST_OPTION_DEBUG4);
+ snprintf(out, outlen, "%d", request->log.lvl);
/*
* Assume we just want to get the current value and NOT set it to 0
level = atoi(fmt);
if (level == 0) {
- request->log.lvl = RAD_REQUEST_OPTION_NONE;
+ request->log.lvl = RAD_REQUEST_LVL_NONE;
request->log.func = NULL;
} else {
if (level > 4) level = 4;
* @param[in] instance of module that's registering the xlat function.
* @return 0 on success, -1 on failure
*/
-int xlat_register(char const *name, RAD_XLAT_FUNC func, RADIUS_ESCAPE_STRING escape, void *instance)
+int xlat_register(char const *name, xlat_func_t func, xlat_escape_t escape, void *instance)
{
- xlat_t *c;
- xlat_t my_xlat;
+ xlat_t *c;
+ xlat_t my_xlat;
rbnode_t *node;
if (!name || !*name) {
int i;
#endif
- xlat_root = rbtree_create(NULL, xlat_cmp, free, 0);
+ xlat_root = rbtree_create(NULL, xlat_cmp, NULL, RBTREE_FLAG_REPLACE);
if (!xlat_root) {
DEBUG("xlat_register: Failed to create tree");
return -1;
XLAT_REGISTER(length);
XLAT_REGISTER(hex);
XLAT_REGISTER(tag);
+ XLAT_REGISTER(vendor);
+ XLAT_REGISTER(vendor_num);
+ XLAT_REGISTER(attr);
+ XLAT_REGISTER(attr_num);
XLAT_REGISTER(string);
XLAT_REGISTER(xlat);
+ XLAT_REGISTER(map);
XLAT_REGISTER(module);
XLAT_REGISTER(debug_attr);
+#if defined(HAVE_REGEX) && defined(HAVE_PCRE)
+ XLAT_REGISTER(regex);
+#endif
xlat_register("debug", xlat_debug, NULL, &xlat_inst[0]);
c = xlat_find("debug");
/*
* Doesn't exist. Create it.
*/
- c = rad_malloc(sizeof(*c));
- memset(c, 0, sizeof(*c));
+ c = talloc_zero(xlat_root, xlat_t);
c->func = func;
c->escape = escape;
return -1;
}
+ /*
+ * Ensure that the data is deleted when the node is
+ * deleted.
+ *
+ * @todo: Maybe this should be the other way around...
+ * when a thing IN the tree is deleted, it's automatically
+ * removed from the tree. But for now, this works.
+ */
+ (void) talloc_steal(node, c);
return 0;
}
* @param[in] func unused.
* @param[in] instance data.
*/
-void xlat_unregister(char const *name, UNUSED RAD_XLAT_FUNC func, void *instance)
+void xlat_unregister(char const *name, UNUSED xlat_func_t func, void *instance)
{
xlat_t *c;
xlat_t my_xlat;
- if (!name) return;
+ if (!name || !xlat_root) return;
strlcpy(my_xlat.name, name, sizeof(my_xlat.name));
my_xlat.length = strlen(my_xlat.name);
rbtree_deletebydata(xlat_root, c);
}
+static int xlat_unregister_callback(void *instance, void *data)
+{
+ xlat_t *c = (xlat_t *) data;
+
+ if (c->instance != instance) return 0; /* keep walking */
+
+ return 2; /* delete it */
+}
+
+void xlat_unregister_module(void *instance)
+{
+ rbtree_walk(xlat_root, RBTREE_DELETE_ORDER, xlat_unregister_callback, instance);
+}
+
+/*
+ * Internal redundant handler for xlats
+ */
+typedef enum xlat_redundant_type_t {
+ XLAT_INVALID = 0,
+ XLAT_REDUNDANT,
+ XLAT_LOAD_BALANCE,
+ XLAT_REDUNDANT_LOAD_BALANCE,
+} xlat_redundant_type_t;
+
+typedef struct xlat_redundant_t {
+ xlat_redundant_type_t type;
+ uint32_t count;
+ CONF_SECTION *cs;
+} xlat_redundant_t;
+
+
+static ssize_t xlat_redundant(void *instance, REQUEST *request,
+ char const *fmt, char *out, size_t outlen)
+{
+ xlat_redundant_t *xr = instance;
+ CONF_ITEM *ci;
+ char const *name;
+ xlat_t *xlat;
+
+ rad_assert(xr->type == XLAT_REDUNDANT);
+
+ /*
+ * Pick the first xlat which succeeds
+ */
+ for (ci = cf_item_find_next(xr->cs, NULL);
+ ci != NULL;
+ ci = cf_item_find_next(xr->cs, ci)) {
+ ssize_t rcode;
+
+ if (!cf_item_is_pair(ci)) continue;
+
+ name = cf_pair_attr(cf_item_to_pair(ci));
+ rad_assert(name != NULL);
+
+ xlat = xlat_find(name);
+ if (!xlat) continue;
+
+ rcode = xlat->func(xlat->instance, request, fmt, out, outlen);
+ if (rcode <= 0) continue;
+ return rcode;
+ }
+
+ /*
+ * Everything failed. Oh well.
+ */
+ *out = 0;
+ return 0;
+}
+
+
+static ssize_t xlat_load_balance(void *instance, REQUEST *request,
+ char const *fmt, char *out, size_t outlen)
+{
+ uint32_t count = 0;
+ xlat_redundant_t *xr = instance;
+ CONF_ITEM *ci;
+ CONF_ITEM *found = NULL;
+ char const *name;
+ xlat_t *xlat;
+
+ /*
+ * Choose a child at random.
+ */
+ for (ci = cf_item_find_next(xr->cs, NULL);
+ ci != NULL;
+ ci = cf_item_find_next(xr->cs, ci)) {
+ if (!cf_item_is_pair(ci)) continue;
+ count++;
+
+ /*
+ * Replace the previously found one with a random
+ * new one.
+ */
+ if ((count * (fr_rand() & 0xffff)) < (uint32_t) 0x10000) {
+ found = ci;
+ }
+ }
+
+ /*
+ * Plain load balancing: do one child, and only one child.
+ */
+ if (xr->type == XLAT_LOAD_BALANCE) {
+ name = cf_pair_attr(cf_item_to_pair(found));
+ rad_assert(name != NULL);
+
+ xlat = xlat_find(name);
+ if (!xlat) return -1;
+
+ return xlat->func(xlat->instance, request, fmt, out, outlen);
+ }
+
+ rad_assert(xr->type == XLAT_REDUNDANT_LOAD_BALANCE);
+
+ /*
+ * Try the random one we found. If it fails, keep going
+ * through the rest of the children.
+ */
+ ci = found;
+ do {
+ name = cf_pair_attr(cf_item_to_pair(ci));
+ rad_assert(name != NULL);
+
+ xlat = xlat_find(name);
+ if (xlat) {
+ ssize_t rcode;
+
+ rcode = xlat->func(xlat->instance, request, fmt, out, outlen);
+ if (rcode > 0) return rcode;
+ }
+
+ /*
+ * Go to the next one, wrapping around at the end.
+ */
+ ci = cf_item_find_next(xr->cs, ci);
+ if (!ci) ci = cf_item_find_next(xr->cs, NULL);
+ } while (ci != found);
+
+ return -1;
+}
+
+
+bool xlat_register_redundant(CONF_SECTION *cs)
+{
+ char const *name1, *name2;
+ xlat_redundant_t *xr;
+
+ name1 = cf_section_name1(cs);
+ name2 = cf_section_name2(cs);
+
+ if (!name2) return false;
+
+ if (xlat_find(name2)) {
+ cf_log_err_cs(cs, "An expansion is already registered for this name");
+ return false;
+ }
+
+ xr = talloc_zero(cs, xlat_redundant_t);
+ if (!xr) return false;
+
+ if (strcmp(name1, "redundant") == 0) {
+ xr->type = XLAT_REDUNDANT;
+
+ } else if (strcmp(name1, "redundant-load-balance") == 0) {
+ xr->type = XLAT_REDUNDANT_LOAD_BALANCE;
+
+ } else if (strcmp(name1, "load-balance") == 0) {
+ xr->type = XLAT_LOAD_BALANCE;
+
+ } else {
+ return false;
+ }
+
+ xr->cs = cs;
+
+ /*
+ * Get the number of children for load balancing.
+ */
+ if (xr->type == XLAT_REDUNDANT) {
+ if (xlat_register(name2, xlat_redundant, NULL, xr) < 0) {
+ talloc_free(xr);
+ return false;
+ }
+
+ } else {
+ CONF_ITEM *ci;
+
+ for (ci = cf_item_find_next(cs, NULL);
+ ci != NULL;
+ ci = cf_item_find_next(cs, ci)) {
+ if (!cf_item_is_pair(ci)) continue;
+
+ if (!xlat_find(cf_pair_attr(cf_item_to_pair(ci)))) {
+ talloc_free(xr);
+ return false;
+ }
+
+ xr->count++;
+ }
+
+ if (xlat_register(name2, xlat_load_balance, NULL, xr) < 0) {
+ talloc_free(xr);
+ return false;
+ }
+ }
+
+ return true;
+}
+
/** Crappy temporary function to add attribute ref support to xlats
*
rbtree_free(xlat_root);
}
-
#ifdef DEBUG_XLAT
# define XLAT_DEBUG DEBUG3
#else
static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
char const **error);
static ssize_t xlat_tokenize_literal(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
- int brace, char const **error);
+ bool brace, char const **error);
static size_t xlat_process(char **out, REQUEST *request, xlat_exp_t const * const head,
- RADIUS_ESCAPE_STRING escape, void *escape_ctx);
+ xlat_escape_t escape, void *escape_ctx);
static ssize_t xlat_tokenize_alternation(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
char const **error)
char const **error)
{
ssize_t slen;
- char *p, *q, *brace;
- char const *attrname;
+ char *p, *q;
xlat_exp_t *node;
+ long num;
rad_assert(fmt[0] == '%');
rad_assert(fmt[1] == '{');
/*
* %{%{...}:-bar}
*/
- if ((fmt[2] == '%') && (fmt[3] == '{')) {
- return xlat_tokenize_alternation(ctx, fmt, head, error);
- }
+ if ((fmt[2] == '%') && (fmt[3] == '{')) return xlat_tokenize_alternation(ctx, fmt, head, error);
XLAT_DEBUG("EXPANSION <-- %s", fmt);
node = talloc_zero(ctx, xlat_exp_t);
- attrname = node->fmt = fmt + 2;
+ node->fmt = fmt + 2;
node->len = 0;
#ifdef HAVE_REGEX
/*
* Handle regex's specially.
*/
- if (isdigit((int) fmt[2]) && (fmt[3] == '}')) {
- if (fmt[2] == '9') {
+ p = fmt + 2;
+ num = strtol(p, &q, 10);
+ if (p != q && (*q == '}')) {
+ XLAT_DEBUG("REGEX <-- %s", fmt);
+ *q = '\0';
+
+ if ((num > REQUEST_MAX_REGEX) || (num < 0)) {
talloc_free(node);
- *error = "Invalid regex reference";
+ *error = "Invalid regex reference. Must be in range 0-" STRINGIFY(REQUEST_MAX_REGEX);
return -2;
}
-
- XLAT_DEBUG("REGEX <-- %s", fmt);
- fmt[3] = '\0';
- node->attr.tmpl_num = fmt[2] - '0'; /* ASCII */
+ node->attr.tmpl_num = num;
node->type = XLAT_REGEX;
*head = node;
- return 4;
+
+ return (q - fmt) + 1;
}
#endif /* HAVE_REGEX */
* %{request:Tunnel-Password:1[#]}
* %{mod:foo}
*/
- brace = NULL;
- for (p = fmt + 2; *p != '\0'; p++) {
- if (*p == ':') break;
- if (isspace((int) *p)) break;
+ /*
+ * This is for efficiency, so we don't search for an xlat,
+ * when what's being referenced is obviously an attribute.
+ */
+ p = fmt + 2;
+ for (q = p; *q != '\0'; q++) {
+ if (*q == ':') break;
+
+ if (isspace((int) *q)) break;
- if (*p == '[') break;
+ if (*q == '[') continue;
- if (*p == '}') break;
+ if (*q == '}') break;
}
- if (*p != ':') p = NULL;
+ /*
+ * Check for empty expressions %{}
+ */
+ if ((*q == '}') && (q == p)) {
+ talloc_free(node);
+ *error = "Empty expression is invalid";
+ return -(p - fmt);
+ }
/*
* Might be a module name reference.
+ *
+ * If it's not, it's an attribute or parse error.
*/
- if (p) {
- *p = '\0';
-
- /*
- * %{mod:foo}
- */
+ if (*q == ':') {
+ *q = '\0';
node->xlat = xlat_find(node->fmt);
if (node->xlat) {
+ /*
+ * %{mod:foo}
+ */
node->type = XLAT_MODULE;
- XLAT_DEBUG("MOD <-- %s ... %s", node->fmt, p + 1);
+ p = q + 1;
+ XLAT_DEBUG("MOD <-- %s ... %s", node->fmt, p);
- slen = xlat_tokenize_literal(node, p + 1, &node->child, true, error);
- if (slen <= 0) {
+ slen = xlat_tokenize_literal(node, p, &node->child, true, error);
+ if (slen < 0) {
talloc_free(node);
return slen - (p - fmt);
}
- p += slen + 1;
+ p += slen;
*head = node;
rad_assert(node->next == NULL);
+
return p - fmt;
}
+ *q = ':'; /* Avoids a strdup */
+ }
+ /*
+ * The first token ends with:
+ * - '[' - Which is an attribute index, so it must be an attribute.
+ * - '}' - The end of the expansion, which means it was a bareword.
+ */
+ slen = tmpl_from_attr_substr(&node->attr, p, REQUEST_CURRENT, PAIR_LIST_REQUEST, true, true);
+ if (slen <= 0) {
/*
- * Modules can have '}' in their RHS, so we
- * didn't check for that until now.
- *
- * As of now, node->fmt MUST be a reference to an
- * attribute, however complicated. So it MUST have a closing brace.
- */
- brace = strchr(p + 1, '}');
- if (!brace) goto no_brace;
- *brace = '\0';
-
- /*
- * %{User-Name}
- * %{User-Name[1]}
- * %{Tunnel-Password:1}
- * %{request:Tunnel-Password:1}
- *
- * <sigh> The syntax is fairly poor.
- */
- XLAT_DEBUG("Looking for list in '%s'", attrname);
-
- /*
- * Not a module. Has to be an attribute
- * reference.
- *
- * As of v3, we've removed %{request: ..>} as
- * internally registered xlats.
+ * If the parse error occurred before the ':'
+ * then the error is changed to 'Unknown module',
+ * as it was more likely to be a bad module name,
+ * than a request qualifier.
*/
- *p = ':';
- node->attr.tmpl_request = radius_request_name(&attrname, REQUEST_CURRENT);
- rad_assert(node->attr.tmpl_request != REQUEST_UNKNOWN);
-
- node->attr.tmpl_list = radius_list_name(&attrname, PAIR_LIST_REQUEST);
- if (node->attr.tmpl_list == PAIR_LIST_UNKNOWN) {
- talloc_free(node);
+ if ((*q == ':') && ((p + (slen * -1)) < q)) {
*error = "Unknown module";
- return -2;
- }
-
- /*
- * Check for a trailing tag.
- */
- p = strchr(attrname, ':');
- if (p) *p = '\0';
-
- } else {
- brace = strchr(attrname, '}');
- if (!brace) {
- no_brace:
- talloc_free(node);
- *error = "No matching closing brace";
- return -1; /* second character of format string */
+ } else {
+ *error = fr_strerror();
}
- *brace = '\0';
- node->attr.tmpl_request = REQUEST_CURRENT;
- node->attr.tmpl_list = PAIR_LIST_REQUEST;
- }
-
- *brace = '\0';
-
- XLAT_DEBUG("Looking for attribute name in %s", attrname);
-
- /*
- * Allow for an array reference. They come AFTER the
- * tag, if the tag exists. Otherwise, they come after
- * the attribute name.
- */
- if (p) {
- q = strchr(p + 1, '[');
- } else {
- q = strchr(attrname, '[');
- }
- if (q) *(q++) = '\0';
-
- if (!*attrname) {
talloc_free(node);
- *error = "Empty expression is invalid";
- return -(attrname - fmt);
+ return slen - (p - fmt);
}
/*
- * It's either an attribute name, or a Tunnel-Password:TAG
- * with the ':' already set to NULL.
+ * Might be a virtual XLAT attribute
*/
- node->attr.tmpl_da = dict_attrbyname(attrname);
- if (!node->attr.tmpl_da) {
- /*
- * Foreach. Maybe other stuff, too.
- */
- node->xlat = xlat_find(attrname);
+ if (node->attr.type == TMPL_TYPE_ATTR_UNDEFINED) {
+ node->xlat = xlat_find(node->attr.tmpl_unknown_name);
+ if (node->xlat && node->xlat->instance && !node->xlat->internal) {
+ talloc_free(node);
+ *error = "Missing content in expansion";
+ return -(p - fmt) - slen;
+ }
+
if (node->xlat) {
node->type = XLAT_VIRTUAL;
- node->fmt = attrname;
+ node->fmt = node->attr.tmpl_unknown_name;
XLAT_DEBUG("VIRTUAL <-- %s", node->fmt);
*head = node;
rad_assert(node->next == NULL);
- brace++;
- return brace - fmt;
+ q++;
+ return q - fmt;
}
talloc_free(node);
*error = "Unknown attribute";
- return -(attrname - fmt);
- }
-
- /*
- * Parse the tag.
- */
- if (p) {
- unsigned long tag;
- char *end;
-
- if (!node->attr.tmpl_da->flags.has_tag) {
- talloc_free(node);
- *error = "Attribute cannot have a tag";
- return - (p - fmt);
- }
-
- tag = strtoul(p + 1, &end, 10);
- p++;
-
- if (tag == ULONG_MAX) {
- talloc_free(node);
- *error = "Invalid tag value";
- return - (p - fmt);
- }
-
- node->attr.tmpl_tag = tag;
- p = end;
-
- if (*p) {
- talloc_free(node);
- *error = "Unexpected text after tag";
- return - (p - fmt);
- }
-
- } else {
- node->attr.tmpl_tag = TAG_ANY;
- /* leave p alone */
- }
-
- /*
- * Check for array reference
- */
- if (q) {
- unsigned long num;
- char *end;
-
- p = q;
- if (*p== '#') {
- node->attr.tmpl_num = NUM_COUNT;
- p++;
-
- } else if (*p == '*') {
- node->attr.tmpl_num = NUM_ALL;
- p++;
-
- } else if (isdigit((int) *p)) {
- num = strtoul(p, &end, 10);
- if (num > 1000) {
- talloc_free(node);
- *error = "Invalid array index";
- return - (p - fmt);
- }
- p = end;
- node->attr.tmpl_num = num;
-
- } else {
- talloc_free(node);
- *error = "Invalid array index";
- return - (p - fmt);
- }
-
- if (*p != ']') {
- talloc_free(node);
- *error = "Expected ']'";
- return - (p - fmt);
- }
-
- p++;
- if (*p) {
- talloc_free(node);
- *error = "Unexpected text after array reference";
- return - (p - fmt);
- }
- } else {
- node->attr.tmpl_num = NUM_ANY;
+ return -(p - fmt);
}
- rad_assert(!p || (p == brace));
-
node->type = XLAT_ATTRIBUTE;
- p = brace + 1;
+ p += slen;
+ if (*p != '}') {
+ talloc_free(node);
+ *error = "No matching closing brace";
+ return -1; /* second character of format string */
+ }
+ *p++ = '\0';
*head = node;
rad_assert(node->next == NULL);
+
return p - fmt;
}
static ssize_t xlat_tokenize_literal(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
- int brace, char const **error)
+ bool brace, char const **error)
{
char *p;
xlat_exp_t *node;
*error = "Invalid escape at end of string";
return -(p - fmt);
}
+
p += 2;
+ node->len += 2;
continue;
}
if ((p[0] == '%') && (p[1] == '{')) {
ssize_t slen;
- XLAT_DEBUG("LITERAL <-- %s", node->fmt);
+ XLAT_DEBUG("EXPANSION-2 <-- %s", node->fmt);
slen = xlat_tokenize_expansion(node, p, &node->next, error);
if (slen <= 0) {
return slen - (p - fmt);
}
+ brace = false; /* it was found above, or else the above code errored out */
p += slen;
break; /* stop processing the string */
}
ssize_t slen;
xlat_exp_t *next;
- if (!p[1] || !strchr("%dlmtDGHISTYv", p[1])) {
- talloc_free(node);
- *error = "Invalid variable expansion";
- p++;
- return - (p - fmt);
+ if (!p[1] || !strchr("%}dlmntDGHISTYv", p[1])) {
+ talloc_free(node);
+ *error = "Invalid variable expansion";
+ p++;
+ return - (p - fmt);
}
next = talloc_zero(node, xlat_exp_t);
next->len = 1;
- if (p[1] == '%') {
- next->fmt = talloc_typed_strdup(next, "%");
+ switch (p[1]) {
+ case '%':
+ case '}':
+ next->fmt = talloc_strndup(next, p + 1, 1);
- XLAT_DEBUG("LITERAL <-- %s", next->fmt);
+ XLAT_DEBUG("LITERAL-ESCAPED <-- %s", next->fmt);
next->type = XLAT_LITERAL;
+ break;
- } else {
+ default:
next->fmt = p + 1;
XLAT_DEBUG("PERCENT <-- %c", *next->fmt);
next->type = XLAT_PERCENT;
+ break;
}
node->next = next;
return slen - (p - fmt);
}
+ brace = false; /* it was found above, or else the above code errored out */
p += slen;
break; /* stop processing the string */
}
* If required, eat the brace.
*/
if (brace && (*p == '}')) {
+ brace = false;
*p = '\0';
p++;
break;
}
/*
+ * We were told to look for a brace, but we ran off of
+ * the end of the string before we found one.
+ */
+ if (brace) {
+ *error = "Missing closing brace at end of string";
+ return -(p - fmt);
+ }
+
+ /*
* Squash zero-width literals
*/
if (node->len > 0) {
*head = node;
+
} else {
- *head = talloc_steal(ctx, node->next);
+ (void) talloc_steal(ctx, node->next);
+ *head = node->next;
talloc_free(node);
}
#endif
case XLAT_ALTERNATE:
- DEBUG("%.*sif {", lvl, xlat_tabs);
+ DEBUG("%.*sXLAT-IF {", lvl, xlat_tabs);
xlat_tokenize_debug(node->child, lvl + 1);
DEBUG("%.*s}", lvl, xlat_tabs);
- DEBUG("%.*selse {", lvl, xlat_tabs);
+ DEBUG("%.*sXLAT-ELSE {", lvl, xlat_tabs);
xlat_tokenize_debug(node->alternate, lvl + 1);
DEBUG("%.*s}", lvl, xlat_tabs);
break;
{
ssize_t slen;
char *tokens;
- char const *error;
+ char const *error = NULL;
*head = NULL;
return slen;
}
- if (*head && (debug_flag > 2)) {
+ if (*head && (rad_debug_lvl > 2)) {
DEBUG("%s", fmt);
DEBUG("Parsed xlat tree:");
xlat_tokenize_debug(*head, 0);
}
-static char *xlat_getvp(TALLOC_CTX *ctx, REQUEST *request, pair_lists_t list, DICT_ATTR const *da,
- int8_t tag, int num, bool return_null)
+static char *xlat_getvp(TALLOC_CTX *ctx, REQUEST *request, vp_tmpl_t const *vpt,
+ bool escape, bool return_null)
{
- VALUE_PAIR *vp, *vps = NULL, *myvp = NULL;
+ VALUE_PAIR *vp = NULL, *virtual = NULL;
RADIUS_PACKET *packet = NULL;
DICT_VALUE *dv;
char *ret = NULL;
- /*
- * Arg. Too much abstraction is annoying.
- */
- switch (list) {
- default:
- if (return_null) return NULL;
- return vp_aprint_type(ctx, da->type);
-
- case PAIR_LIST_CONTROL:
- vps = request->config_items;
- break;
-
- case PAIR_LIST_REQUEST:
- packet = request->packet;
- if (packet) vps = packet->vps;
- break;
-
- case PAIR_LIST_REPLY:
- packet = request->reply;
- if (packet) vps = packet->vps;
- break;
-
-#ifdef WITH_PROXY
- case PAIR_LIST_PROXY_REQUEST:
- packet = request->proxy;
- if (packet) vps = packet->vps;
- break;
-
- case PAIR_LIST_PROXY_REPLY:
- packet = request->proxy_reply;
- if (packet) vps = packet->vps;
- break;
-#endif
-
-#ifdef WITH_COA
- case PAIR_LIST_COA:
- case PAIR_LIST_DM:
- if (request->coa) packet = request->coa->packet;
- if (packet) vps = packet->vps;
- break;
+ vp_cursor_t cursor;
+ char quote = escape ? '"' : '\0';
- case PAIR_LIST_COA_REPLY:
- case PAIR_LIST_DM_REPLY:
- if (request->coa) packet = request->coa->reply;
- if (packet) vps = packet->vps;
- break;
+ rad_assert((vpt->type == TMPL_TYPE_ATTR) || (vpt->type == TMPL_TYPE_LIST));
-#endif
+ /*
+ * We only support count and concatenate operations on lists.
+ */
+ if (vpt->type == TMPL_TYPE_LIST) {
+ vp = tmpl_cursor_init(NULL, &cursor, request, vpt);
+ goto do_print;
}
/*
- * Now we have the list, check to see if we have an attribute in
- * the request, if we do, it takes precedence over the virtual
- * attributes.
+ * See if we're dealing with an attribute in the request
*
- * This allows users to manipulate virtual attributes as if they
- * were real ones.
+ * This allows users to manipulate virtual attributes as if
+ * they were real ones.
*/
- vp = pairfind(vps, da->attr, da->vendor, tag);
+ vp = tmpl_cursor_init(NULL, &cursor, request, vpt);
if (vp) goto do_print;
/*
- * We didn't find the VP in a list. It MIGHT be a
- * virtual one, in which case we do lots more checks
- * below. However, if we're looking for a normal
- * attribute, it must exist, and therefore not finding it
- * means we return NULL.
+ * We didn't find the VP in a list.
+ * If it's not a virtual one, and we're not meant to
+ * be counting it, return.
+ */
+ if (!vpt->tmpl_da->flags.virtual) {
+ if (vpt->tmpl_num == NUM_COUNT) goto do_print;
+ return NULL;
+ }
+
+ /*
+ * Switch out the request to the one specified by the template
*/
- if (!da->flags.virtual) return NULL;
+ if (radius_request(&request, vpt->tmpl_request) < 0) return NULL;
/*
* Some non-packet expansions
*/
- switch (da->attr) {
+ switch (vpt->tmpl_da->attr) {
default:
break; /* ignore them */
case PW_CLIENT_SHORTNAME:
- if (num == NUM_COUNT) goto count;
+ if (vpt->tmpl_num == NUM_COUNT) goto count_virtual;
if (request->client && request->client->shortname) {
return talloc_typed_strdup(ctx, request->client->shortname);
}
return talloc_typed_strdup(ctx, "<UNKNOWN-CLIENT>");
case PW_REQUEST_PROCESSING_STAGE:
- if (num == NUM_COUNT) goto count;
+ if (vpt->tmpl_num == NUM_COUNT) goto count_virtual;
if (request->component) {
return talloc_typed_strdup(ctx, request->component);
}
return talloc_typed_strdup(ctx, "server_core");
case PW_VIRTUAL_SERVER:
- if (num == NUM_COUNT) goto count;
+ if (vpt->tmpl_num == NUM_COUNT) goto count_virtual;
if (!request->server) return NULL;
return talloc_typed_strdup(ctx, request->server);
case PW_MODULE_RETURN_CODE:
- if (num == NUM_COUNT) goto count;
+ if (vpt->tmpl_num == NUM_COUNT) goto count_virtual;
if (!request->rcode) return NULL;
return talloc_typed_strdup(ctx, fr_int2str(modreturn_table, request->rcode, ""));
}
* If there's no packet, we can't print any attribute
* referencing it.
*/
+ packet = radius_packet(request, vpt->tmpl_list);
if (!packet) {
if (return_null) return NULL;
- return vp_aprint_type(ctx, da->type);
+ return vp_aprints_type(ctx, vpt->tmpl_da->type);
}
vp = NULL;
- switch (da->attr) {
+ switch (vpt->tmpl_da->attr) {
default:
break;
* various VP functions.
*/
case PW_PACKET_AUTHENTICATION_VECTOR:
- myvp = pairalloc(ctx, da);
- pairmemcpy(myvp, packet->vector, sizeof(packet->vector));
- vp = myvp;
+ virtual = fr_pair_afrom_da(ctx, vpt->tmpl_da);
+ fr_pair_value_memcpy(virtual, packet->vector, sizeof(packet->vector));
+ vp = virtual;
break;
case PW_CLIENT_IP_ADDRESS:
case PW_PACKET_SRC_IP_ADDRESS:
if (packet->src_ipaddr.af == AF_INET) {
- myvp = pairalloc(ctx, da);
- myvp->vp_ipaddr = packet->src_ipaddr.ipaddr.ip4addr.s_addr;
- vp = myvp;
+ virtual = fr_pair_afrom_da(ctx, vpt->tmpl_da);
+ virtual->vp_ipaddr = packet->src_ipaddr.ipaddr.ip4addr.s_addr;
+ vp = virtual;
}
break;
case PW_PACKET_DST_IP_ADDRESS:
if (packet->dst_ipaddr.af == AF_INET) {
- myvp = pairalloc(ctx, da);
- myvp->vp_ipaddr = packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
- vp = myvp;
+ virtual = fr_pair_afrom_da(ctx, vpt->tmpl_da);
+ virtual->vp_ipaddr = packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
+ vp = virtual;
}
break;
case PW_PACKET_SRC_IPV6_ADDRESS:
if (packet->src_ipaddr.af == AF_INET6) {
- myvp = pairalloc(ctx, da);
- memcpy(&myvp->vp_ipv6addr,
+ virtual = fr_pair_afrom_da(ctx, vpt->tmpl_da);
+ memcpy(&virtual->vp_ipv6addr,
&packet->src_ipaddr.ipaddr.ip6addr,
sizeof(packet->src_ipaddr.ipaddr.ip6addr));
- vp = myvp;
+ vp = virtual;
}
break;
case PW_PACKET_DST_IPV6_ADDRESS:
if (packet->dst_ipaddr.af == AF_INET6) {
- myvp = pairalloc(ctx, da);
- memcpy(&myvp->vp_ipv6addr,
+ virtual = fr_pair_afrom_da(ctx, vpt->tmpl_da);
+ memcpy(&virtual->vp_ipv6addr,
&packet->dst_ipaddr.ipaddr.ip6addr,
sizeof(packet->dst_ipaddr.ipaddr.ip6addr));
- vp = myvp;
+ vp = virtual;
}
break;
case PW_PACKET_SRC_PORT:
- myvp = pairalloc(ctx, da);
- myvp->vp_integer = packet->src_port;
- vp = myvp;
+ virtual = fr_pair_afrom_da(ctx, vpt->tmpl_da);
+ virtual->vp_integer = packet->src_port;
+ vp = virtual;
break;
case PW_PACKET_DST_PORT:
- myvp = pairalloc(ctx, da);
- myvp->vp_integer = packet->dst_port;
- vp = myvp;
+ virtual = fr_pair_afrom_da(ctx, vpt->tmpl_da);
+ virtual->vp_integer = packet->dst_port;
+ vp = virtual;
break;
}
/*
* Fake various operations for virtual attributes.
*/
- if (myvp) {
- if (num != NUM_ANY) switch (num) {
+ if (virtual) {
+ if (vpt->tmpl_num != NUM_ANY) switch (vpt->tmpl_num) {
/*
* [n] is NULL (we only have [0])
*/
* [#] means 1 (as there's only one)
*/
case NUM_COUNT:
- count:
+ count_virtual:
ret = talloc_strdup(ctx, "1");
goto finish;
}
do_print:
+ switch (vpt->tmpl_num) {
/*
- * We want the N'th VP.
+ * Return a count of the VPs.
*/
- if (num != NUM_ANY) {
+ case NUM_COUNT:
+ {
int count = 0;
- vp_cursor_t cursor;
- switch (num) {
- /*
- * Return a count of the VPs.
- */
- case NUM_COUNT:
- fr_cursor_init(&cursor, &vp);
- while (fr_cursor_next_by_da(&cursor, da, tag) != NULL) {
- count++;
- }
- return talloc_typed_asprintf(ctx, "%d", count);
+ for (vp = tmpl_cursor_init(NULL, &cursor, request, vpt);
+ vp;
+ vp = tmpl_cursor_next(&cursor, vpt)) count++;
- /*
- * Ugly, but working.
- */
- case NUM_ALL:
- {
- char *p, *q;
-
- (void) fr_cursor_init(&cursor, &vp);
- vp = fr_cursor_next_by_da(&cursor, da, tag);
- if (!vp) return NULL;
-
- p = vp_aprint_value(ctx, vp, '"');
- if (!p) return NULL;
- while ((vp = fr_cursor_next_by_da(&cursor, da, tag)) != NULL) {
- q = vp_aprint_value(ctx, vp, '"');
- if (!q) return NULL;
- p = talloc_strdup_append(p, ",");
- p = talloc_strdup_append(p, q);
- }
+ return talloc_typed_asprintf(ctx, "%d", count);
+ }
- return p;
- }
- default:
- fr_cursor_init(&cursor, &vp);
- while ((vp = fr_cursor_next_by_da(&cursor, da, tag)) != NULL) {
- if (count++ == num) break;
- }
- break;
+ /*
+ * Concatenate all values together,
+ * separated by commas.
+ */
+ case NUM_ALL:
+ {
+ char *p, *q;
+
+ if (!fr_cursor_current(&cursor)) return NULL;
+ p = vp_aprints_value(ctx, vp, quote);
+ if (!p) return NULL;
+
+ while ((vp = tmpl_cursor_next(&cursor, vpt)) != NULL) {
+ q = vp_aprints_value(ctx, vp, quote);
+ if (!q) return NULL;
+ p = talloc_strdup_append(p, ",");
+ p = talloc_strdup_append(p, q);
}
+
+ return p;
+ }
+
+ default:
+ /*
+ * The cursor was set to the correct
+ * position above by tmpl_cursor_init.
+ */
+ vp = fr_cursor_current(&cursor);
+ break;
}
if (!vp) {
if (return_null) return NULL;
- return vp_aprint_type(ctx, da->type);
+ return vp_aprints_type(ctx, vpt->tmpl_da->type);
}
print:
- ret = vp_aprint_value(ctx, vp, '"');
+ ret = vp_aprints_value(ctx, vp, quote);
finish:
- talloc_free(myvp);
+ talloc_free(virtual);
return ret;
}
#endif
static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * const node,
- RADIUS_ESCAPE_STRING escape, void *escape_ctx, int lvl)
+ xlat_escape_t escape, void *escape_ctx, int lvl)
{
ssize_t rcode;
char *str = NULL, *child;
- char *q;
char const *p;
- REQUEST *ref;
- XLAT_DEBUG("%.*sxlat aprint %d", lvl, xlat_spaces, node->type);
+ XLAT_DEBUG("%.*sxlat aprint %d %s", lvl, xlat_spaces, node->type, node->fmt);
switch (node->type) {
/*
* Don't escape this.
*/
case XLAT_LITERAL:
- XLAT_DEBUG("xlat_aprint LITERAL");
+ XLAT_DEBUG("%.*sxlat_aprint LITERAL", lvl, xlat_spaces);
return talloc_typed_strdup(ctx, node->fmt);
/*
size_t freespace = 256;
struct tm ts;
time_t when;
+ int usec;
- XLAT_DEBUG("xlat_aprint PERCENT");
+ XLAT_DEBUG("%.*sxlat_aprint PERCENT", lvl, xlat_spaces);
str = talloc_array(ctx, char, freespace); /* @todo do better allocation */
p = node->fmt;
when = request->timestamp;
+ usec = 0;
if (request->packet) {
when = request->packet->timestamp.tv_sec;
+ usec = request->packet->timestamp.tv_usec;
}
switch (*p) {
break;
case 'd': /* request day */
- (void) localtime_r(&when, &ts);
+ if (!localtime_r(&when, &ts)) goto error;
strftime(str, freespace, "%d", &ts);
break;
break;
case 'm': /* request month */
- (void) localtime_r(&when, &ts);
+ if (!localtime_r(&when, &ts)) goto error;
strftime(str, freespace, "%m", &ts);
break;
break;
case 'D': /* request date */
- (void) localtime_r(&when, &ts);
+ if (!localtime_r(&when, &ts)) goto error;
strftime(str, freespace, "%Y%m%d", &ts);
break;
case 'G': /* request minute */
- (void) localtime_r(&when, &ts);
+ if (!localtime_r(&when, &ts)) goto error;
strftime(str, freespace, "%M", &ts);
break;
case 'H': /* request hour */
- (void) localtime_r(&when, &ts);
+ if (!localtime_r(&when, &ts)) goto error;
strftime(str, freespace, "%H", &ts);
break;
break;
case 'S': /* request timestamp in SQL format*/
- (void) localtime_r(&when, &ts);
- strftime(str, freespace, "%Y-%m-%d %H:%M:%S", &ts);
+ if (!localtime_r(&when, &ts)) goto error;
+ nl = str + strftime(str, freespace, "%Y-%m-%d %H:%M:%S", &ts);
+ rad_assert(((str + freespace) - nl) >= 8);
+ snprintf(nl, (str + freespace) - nl, ".%06d", usec);
break;
case 'T': /* request timestamp */
- (void) localtime_r(&when, &ts);
- strftime(str, freespace, "%Y-%m-%d-%H.%M.%S.000000", &ts);
+ if (!localtime_r(&when, &ts)) goto error;
+ strftime(str, freespace, "%Y-%m-%d-%H.%M.%S", &ts);
+
break;
case 'Y': /* request year */
- (void) localtime_r(&when, &ts);
+ if (!localtime_r(&when, &ts)) {
+ error:
+ REDEBUG("Failed converting packet timestamp to localtime: %s", fr_syserror(errno));
+ talloc_free(str);
+ return NULL;
+ }
strftime(str, freespace, "%Y", &ts);
break;
case 'v': /* Version of code */
- snprintf(str, freespace, "%s", radiusd_short_version);
+ RWDEBUG("%%v is deprecated and will be removed. Use ${version.freeradius-server}");
+ snprintf(str, freespace, "%s", radiusd_version_short);
break;
default:
break;
case XLAT_ATTRIBUTE:
- XLAT_DEBUG("xlat_aprint ATTRIBUTE");
- ref = request;
- if (radius_request(&ref, node->attr.tmpl_request) < 0) {
- return NULL;
- }
+ XLAT_DEBUG("%.*sxlat_aprint ATTRIBUTE", lvl, xlat_spaces);
/*
* Some attributes are virtual <sigh>
*/
- str = xlat_getvp(ctx, ref, node->attr.tmpl_list, node->attr.tmpl_da, node->attr.tmpl_tag, node->attr.tmpl_num, true);
+ str = xlat_getvp(ctx, request, &node->attr, escape ? false : true, true);
if (str) {
- XLAT_DEBUG("EXPAND attr %s", node->attr.tmpl_da->name);
- XLAT_DEBUG(" ---> %s", str);
+ XLAT_DEBUG("%.*sEXPAND attr %s", lvl, xlat_spaces, node->attr.tmpl_da->name);
+ XLAT_DEBUG("%.*s ---> %s", lvl ,xlat_spaces, str);
}
break;
talloc_free(str);
return NULL;
}
+ RDEBUG2("EXPAND %s", node->xlat->name);
+ RDEBUG2(" --> %s", str);
break;
case XLAT_MODULE:
XLAT_DEBUG("xlat_aprint MODULE");
- if (xlat_process(&child, request, node->child, node->xlat->escape, node->xlat->instance) == 0) {
- return NULL;
+
+ if (node->child) {
+ if (xlat_process(&child, request, node->child, node->xlat->escape, node->xlat->instance) == 0) {
+ return NULL;
+ }
+
+ XLAT_DEBUG("%.*sEXPAND mod %s %s", lvl, xlat_spaces, node->fmt, node->child->fmt);
+ } else {
+ XLAT_DEBUG("%.*sEXPAND mod %s", lvl, xlat_spaces, node->fmt);
+ child = talloc_typed_strdup(ctx, "");
}
- XLAT_DEBUG("%.*sEXPAND mod %s %s", lvl, xlat_spaces, node->fmt, node->child->fmt);
XLAT_DEBUG("%.*s ---> %s", lvl, xlat_spaces, child);
/*
* Smash \n --> CR.
*
- * The OUTPUT of xlat is a printable string. The INPUT might not be...
+ * The OUTPUT of xlat is a "raw" string. The INPUT is a printable string.
*
- * This is really the reverse of fr_print_string().
+ * This is really the reverse of fr_prints().
*/
- p = q = child;
- while (*p) {
- if (*p == '\\') switch (p[1]) {
- default:
- *(q++) = p[1];
- p += 2;
- continue;
-
- case 'n':
- *(q++) = '\n';
- p += 2;
- continue;
-
- case 't':
- *(q++) = '\t';
- p += 2;
- continue;
+ if (cf_new_escape && *child) {
+ ssize_t slen;
+ PW_TYPE type;
+ value_data_t data;
+
+ type = PW_TYPE_STRING;
+ slen = value_data_from_str(request, &data, &type, NULL, child, talloc_array_length(child) - 1, '"');
+ if (slen <= 0) {
+ talloc_free(child);
+ return NULL;
}
- *(q++) = *(p++);
+ talloc_free(child);
+ child = data.ptr;
+
+ } else {
+ char *q;
+
+ p = q = child;
+ while (*p) {
+ if (*p == '\\') switch (p[1]) {
+ default:
+ *(q++) = p[1];
+ p += 2;
+ continue;
+
+ case 'n':
+ *(q++) = '\n';
+ p += 2;
+ continue;
+
+ case 't':
+ *(q++) = '\t';
+ p += 2;
+ continue;
+ }
+
+ *(q++) = *(p++);
+ }
+ *q = '\0';
}
- *q = '\0';
str = talloc_array(ctx, char, 2048); /* FIXME: have the module call talloc_typed_asprintf */
*str = '\0'; /* Be sure the string is NULL terminated, we now only free on error */
#ifdef HAVE_REGEX
case XLAT_REGEX:
- XLAT_DEBUG("xlat_aprint REGEX");
- child = request_data_reference(request, request,
- REQUEST_DATA_REGEX | node->attr.tmpl_num);
- if (!child) return NULL;
+ XLAT_DEBUG("%.*sxlat_aprint REGEX", lvl, xlat_spaces);
+ if (regex_request_to_sub(ctx, &str, request, node->attr.tmpl_num) < 0) return NULL;
- str = talloc_typed_strdup(ctx, child);
break;
#endif
case XLAT_ALTERNATE:
- XLAT_DEBUG("xlat_aprint ALTERNATE");
+ XLAT_DEBUG("%.*sxlat_aprint ALTERNATE", lvl, xlat_spaces);
rad_assert(node->child != NULL);
rad_assert(node->alternate != NULL);
- str = xlat_aprint(ctx, request, node->child, escape, escape_ctx, lvl);
- if (str) break;
+ /*
+ * If there are no "next" nodes, call ourselves
+ * recursively, which is fast.
+ *
+ * If there are "next" nodes, call xlat_process()
+ * which does a ton more work.
+ */
+ if (!node->next) {
+ str = xlat_aprint(ctx, request, node->child, escape, escape_ctx, lvl);
+ if (str) {
+ XLAT_DEBUG("%.*sALTERNATE got first string: %s", lvl, xlat_spaces, str);
+ } else {
+ str = xlat_aprint(ctx, request, node->alternate, escape, escape_ctx, lvl);
+ XLAT_DEBUG("%.*sALTERNATE got alternate string %s", lvl, xlat_spaces, str);
+ }
+ } else {
- str = xlat_aprint(ctx, request, node->alternate, escape, escape_ctx, lvl);
+ if (xlat_process(&str, request, node->child, escape, escape_ctx) > 0) {
+ XLAT_DEBUG("%.*sALTERNATE got first string: %s", lvl, xlat_spaces, str);
+ } else {
+ (void) xlat_process(&str, request, node->alternate, escape, escape_ctx);
+ XLAT_DEBUG("%.*sALTERNATE got alternate string %s", lvl, xlat_spaces, str);
+ }
+ }
break;
+ }
+ /*
+ * If there's no data, return that, instead of an empty string.
+ */
+ if (str && !str[0]) {
+ talloc_free(str);
+ return NULL;
}
/*
* Escape the non-literals we found above.
*/
if (str && escape) {
+ size_t len;
char *escaped;
- escaped = talloc_array(ctx, char, 2048); /* FIXME: do something intelligent */
- escape(request, escaped, 2038, str, escape_ctx);
+ len = talloc_array_length(str) * 3;
+
+ escaped = talloc_array(ctx, char, len);
+ escape(request, escaped, len, str, escape_ctx);
talloc_free(str);
str = escaped;
}
static size_t xlat_process(char **out, REQUEST *request, xlat_exp_t const * const head,
- RADIUS_ESCAPE_STRING escape, void *escape_ctx)
+ xlat_escape_t escape, void *escape_ctx)
{
int i, list;
size_t total;
/** Replace %whatever in a string.
*
- * See 'doc/variables.txt' for more information.
+ * See 'doc/configuration/variables.rst' for more information.
*
* @param[out] out Where to write pointer to output buffer.
* @param[in] outlen Size of out.
* @return length of string written @bug should really have -1 for failure
*/
static ssize_t xlat_expand_struct(char **out, size_t outlen, REQUEST *request, xlat_exp_t const *node,
- RADIUS_ESCAPE_STRING escape, void *escape_ctx)
+ xlat_escape_t escape, void *escape_ctx)
{
char *buff;
ssize_t len;
return len;
}
+ len = strlen(buff);
+
+ /*
+ * If out doesn't point to an existing buffer
+ * copy the pointer to our buffer over.
+ */
if (!*out) {
*out = buff;
- } else {
- strlcpy(*out, buff, outlen);
- talloc_free(buff);
+ return len;
}
- return strlen(*out);
+ /*
+ * Otherwise copy the malloced buffer to the fixed one.
+ */
+ strlcpy(*out, buff, outlen);
+ talloc_free(buff);
+ return len;
}
static ssize_t xlat_expand(char **out, size_t outlen, REQUEST *request, char const *fmt,
- RADIUS_ESCAPE_STRING escape, void *escape_ctx) CC_HINT(nonnull (1, 3, 4));
+ xlat_escape_t escape, void *escape_ctx) CC_HINT(nonnull (1, 3, 4));
/** Replace %whatever in a string.
*
- * See 'doc/variables.txt' for more information.
+ * See 'doc/configuration/variables.rst' for more information.
*
* @param[out] out Where to write pointer to output buffer.
* @param[in] outlen Size of out.
* @param[in] escape_ctx pointer to pass to escape function.
* @return length of string written @bug should really have -1 for failure
*/
-static ssize_t CC_HINT(nonnull (1, 3, 4)) xlat_expand(char **out, size_t outlen, REQUEST *request, char const *fmt,
- RADIUS_ESCAPE_STRING escape, void *escape_ctx)
+static ssize_t xlat_expand(char **out, size_t outlen, REQUEST *request, char const *fmt,
+ xlat_escape_t escape, void *escape_ctx)
{
ssize_t len;
xlat_exp_t *node;
return len;
}
-/*
- * Try to convert an xlat to a tmpl for efficiency
+/** Try to convert an xlat to a tmpl for efficiency
+ *
+ * @param ctx to allocate new vp_tmpl_t in.
+ * @param node to convert.
+ * @return NULL if unable to convert (not necessarily error), or a new vp_tmpl_t.
*/
-value_pair_tmpl_t *radius_xlat2tmpl(TALLOC_CTX *ctx, xlat_exp_t *node)
+vp_tmpl_t *xlat_to_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_t *node)
{
- value_pair_tmpl_t *vpt;
+ vp_tmpl_t *vpt;
- if (node->next || (node->type != XLAT_ATTRIBUTE)) return NULL;
+ if (node->next || (node->type != XLAT_ATTRIBUTE) || (node->attr.type != TMPL_TYPE_ATTR)) return NULL;
/*
- * @todo it should be possible to emulate the concat and count operations in the
- * map code.
+ * Concat means something completely different as an attribute reference
+ * Count isn't implemented.
*/
if ((node->attr.tmpl_num == NUM_COUNT) || (node->attr.tmpl_num == NUM_ALL)) return NULL;
vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, node->fmt, -1);
if (!vpt) return NULL;
- vpt->tmpl_request = node->attr.tmpl_request;
- vpt->tmpl_list = node->attr.tmpl_list;
- vpt->tmpl_da = node->attr.tmpl_da;
- vpt->tmpl_num = node->attr.tmpl_num;
- vpt->tmpl_tag = node->attr.tmpl_tag;
+ memcpy(&vpt->data, &node->attr.data, sizeof(vpt->data));
VERIFY_TMPL(vpt);
return vpt;
}
-ssize_t radius_xlat(char *out, size_t outlen, REQUEST *request, char const *fmt, RADIUS_ESCAPE_STRING escape, void *ctx)
+/** Try to convert attr tmpl to an xlat for &attr[*] and artificially constructing expansions
+ *
+ * @param ctx to allocate new xlat_expt_t in.
+ * @param vpt to convert.
+ * @return NULL if unable to convert (not necessarily error), or a new vp_tmpl_t.
+ */
+xlat_exp_t *xlat_from_tmpl_attr(TALLOC_CTX *ctx, vp_tmpl_t *vpt)
+{
+ xlat_exp_t *node;
+
+ if (vpt->type != TMPL_TYPE_ATTR) return NULL;
+
+ node = talloc_zero(ctx, xlat_exp_t);
+ node->type = XLAT_ATTRIBUTE;
+ node->fmt = talloc_bstrndup(node, vpt->name, vpt->len);
+ tmpl_init(&node->attr, TMPL_TYPE_ATTR, node->fmt, talloc_array_length(node->fmt) - 1);
+ memcpy(&node->attr.data, &vpt->data, sizeof(vpt->data));
+
+ return node;
+}
+
+ssize_t radius_xlat(char *out, size_t outlen, REQUEST *request, char const *fmt, xlat_escape_t escape, void *ctx)
{
return xlat_expand(&out, outlen, request, fmt, escape, ctx);
}
-ssize_t radius_axlat(char **out, REQUEST *request, char const *fmt, RADIUS_ESCAPE_STRING escape, void *ctx)
+ssize_t radius_xlat_struct(char *out, size_t outlen, REQUEST *request, xlat_exp_t const *xlat, xlat_escape_t escape, void *ctx)
{
+ return xlat_expand_struct(&out, outlen, request, xlat, escape, ctx);
+}
+
+ssize_t radius_axlat(char **out, REQUEST *request, char const *fmt, xlat_escape_t escape, void *ctx)
+{
+ *out = NULL;
return xlat_expand(out, 0, request, fmt, escape, ctx);
}
-ssize_t radius_axlat_struct(char **out, REQUEST *request, xlat_exp_t const *xlat, RADIUS_ESCAPE_STRING escape, void *ctx)
+ssize_t radius_axlat_struct(char **out, REQUEST *request, xlat_exp_t const *xlat, xlat_escape_t escape, void *ctx)
{
+ *out = NULL;
return xlat_expand_struct(out, 0, request, xlat, escape, ctx);
}