/*
- * xlat.c Translate strings. This is the first version of xlat
+ * xlat.c Translate strings. This is the first version of xlat
* incorporated to RADIUS
*
* Version: $Id$
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
- * Copyright 2000 The FreeRADIUS server project
+ * Copyright 2000,2006 The FreeRADIUS server project
* Copyright 2000 Alan DeKok <aland@ox.org>
*/
-static const char rcsid[] =
-"$Id$";
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
-#include "autoconf.h"
-#include "libradius.h"
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/rad_assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
#include <ctype.h>
-#include "radiusd.h"
+typedef struct xlat_t {
+ char module[MAX_STRING_LEN];
+ int length;
+ void *instance;
+ RAD_XLAT_FUNC do_xlat;
+ int internal; /* not allowed to re-define these */
+} xlat_t;
-struct xlat_cmp {
- char module[MAX_STRING_LEN];
- int length;
- void *instance;
- RAD_XLAT_FUNC do_xlat;
- struct xlat_cmp *next;
-};
-
-static struct xlat_cmp *cmp = NULL;
+static rbtree_t *xlat_root = NULL;
/*
- * Register an xlat function.
+ * Define all xlat's in the structure.
*/
-int xlat_register(const char *module, RAD_XLAT_FUNC func, void *instance)
-{
- struct xlat_cmp *c;
+static const char * const internal_xlat[] = {"check",
+ "request",
+ "reply",
+ "proxy-request",
+ "proxy-reply",
+ NULL};
- if (module == NULL || strlen(module) == 0){
- DEBUG("xlat_register: Invalid module name");
- return -1;
- }
+#if REQUEST_MAX_REGEX > 8
+#error Please fix the following line
+#endif
+static const int xlat_inst[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; /* up to 8 for regex */
- xlat_unregister(module, func);
- c = rad_malloc(sizeof(struct xlat_cmp));
+/*
+ * Convert the value on a VALUE_PAIR to string
+ */
+static int valuepair2str(char * out,int outlen,VALUE_PAIR * pair,
+ int type, RADIUS_ESCAPE_STRING func)
+{
+ char buffer[MAX_STRING_LEN * 4];
- c->do_xlat = func;
- strncpy(c->module, module, MAX_STRING_LEN);
- c->length = strlen(c->module);
- c->instance = instance;
- c->next = cmp;
- cmp = c;
+ if (pair != NULL) {
+ vp_prints_value(buffer, sizeof(buffer), pair, -1);
+ return func(out, outlen, buffer);
+ }
- return 0;
+ switch (type) {
+ case PW_TYPE_STRING :
+ strlcpy(out,"_",outlen);
+ break;
+ case PW_TYPE_INTEGER :
+ strlcpy(out,"0",outlen);
+ break;
+ case PW_TYPE_IPADDR :
+ strlcpy(out,"?.?.?.?",outlen);
+ break;
+ case PW_TYPE_IPV6ADDR :
+ strlcpy(out,":?:",outlen);
+ break;
+ case PW_TYPE_DATE :
+ strlcpy(out,"0",outlen);
+ break;
+ default :
+ strlcpy(out,"unknown_type",outlen);
+ }
+ return strlen(out);
}
+
/*
- * Unregister an xlat function.
+ * Dynamically translate for check:, request:, reply:, etc.
*/
-void xlat_unregister(const char *module, RAD_XLAT_FUNC func)
+static int xlat_packet(void *instance, REQUEST *request,
+ char *fmt, char *out, size_t outlen,
+ RADIUS_ESCAPE_STRING func)
{
- struct xlat_cmp *c, *last;
+ DICT_ATTR *da;
+ VALUE_PAIR *vp;
+ VALUE_PAIR *vps = NULL;
+ RADIUS_PACKET *packet = NULL;
+
+ switch (*(int*) instance) {
+ case 0:
+ vps = request->config_items;
+ break;
- last = NULL;
- for (c = cmp; c; c = c->next) {
- if (strncmp(c->module,module,c->length) == 0 && c->do_xlat == func)
- break;
- last = c;
+ case 1:
+ vps = request->packet->vps;
+ packet = request->packet;
+ break;
+
+ case 2:
+ vps = request->reply->vps;
+ packet = request->reply;
+ break;
+
+ case 3:
+ if (request->proxy) vps = request->proxy->vps;
+ packet = request->proxy;
+ break;
+
+ case 4:
+ if (request->proxy_reply) vps = request->proxy_reply->vps;
+ packet = request->proxy_reply;
+ break;
+
+ default: /* WTF? */
+ return 0;
}
- if (c == NULL) return;
+ /*
+ * The "format" string is the attribute name.
+ */
+ da = dict_attrbyname(fmt);
+ if (!da) {
+ int count;
+ const char *p = strchr(fmt, '[');
+ char buffer[256];
+
+ if (!p) return 0;
+ if (strlen(fmt) > sizeof(buffer)) return 0;
+
+ strlcpy(buffer, fmt, p - fmt + 1);
+
+ da = dict_attrbyname(buffer);
+ if (!da) return 0;
+
+ /*
+ * %{Attribute-Name[#]} returns the count of
+ * attributes of that name in the list.
+ */
+ if ((p[1] == '#') && (p[2] == ']')) {
+ count = 0;
+
+ for (vp = pairfind(vps, da->attr);
+ vp != NULL;
+ vp = pairfind(vp->next, da->attr)) {
+ count++;
+ }
+ snprintf(out, outlen, "%d", count);
+ return strlen(out);
+ }
+
+ /*
+ * %{Attribute-Name[*]} returns ALL of the
+ * the attributes, separated by a newline.
+ */
+ if ((p[1] == '*') && (p[2] == ']')) {
+ int total = 0;
+
+ for (vp = pairfind(vps, da->attr);
+ vp != NULL;
+ vp = pairfind(vp->next, da->attr)) {
+ count = valuepair2str(out, outlen - 1, vp, da->type, func);
+ rad_assert(count <= outlen);
+ total += count + 1;
+ outlen -= (count + 1);
+ out += count;
+
+ *(out++) = '\n';
+
+ if (outlen == 0) break;
+ }
+
+ return total;
+ }
+
+ count = atoi(p + 1);
- if (last != NULL)
- last->next = c->next;
- else
- cmp = c->next;
+ /*
+ * Skip the numbers.
+ */
+ p += 1 + strspn(p + 1, "0123456789");
+ if (*p != ']') {
+ DEBUG2("xlat: Invalid array reference in string at %s %s",
+ fmt, p);
+ return 0;
+ }
- free(c);
+ /*
+ * Find the N'th value.
+ */
+ for (vp = pairfind(vps, da->attr);
+ vp != NULL;
+ vp = pairfind(vp->next, da->attr)) {
+ if (count == 0) break;
+ count--;
+ }
+
+ /*
+ * Non-existent array reference.
+ */
+ if (!vp) return 0;
+
+ return valuepair2str(out, outlen, vp, da->type, func);
+ }
+
+ vp = pairfind(vps, da->attr);
+ if (!vp) {
+ /*
+ * Some "magic" handlers, which are never in VP's, but
+ * which are in the packet.
+ *
+ * FIXME: We should really do this in a more
+ * intelligent way...
+ */
+ if (packet) {
+ VALUE_PAIR localvp;
+
+ localvp.vp_strvalue[0] = 0;
+
+ switch (da->attr) {
+ case PW_PACKET_TYPE:
+ {
+ DICT_VALUE *dval;
+
+ dval = dict_valbyattr(da->attr, packet->code);
+ if (dval) {
+ snprintf(out, outlen, "%s", dval->name);
+ } else {
+ snprintf(out, outlen, "%d", packet->code);
+ }
+ return strlen(out);
+ }
+ break;
+
+ case PW_CLIENT_IP_ADDRESS: /* the same as below */
+ case PW_PACKET_SRC_IP_ADDRESS:
+ if (packet->src_ipaddr.af != AF_INET) {
+ return 0;
+ }
+ localvp.attribute = da->attr;
+ localvp.lvalue = packet->src_ipaddr.ipaddr.ip4addr.s_addr;
+ break;
+
+ case PW_PACKET_DST_IP_ADDRESS:
+ if (packet->dst_ipaddr.af != AF_INET) {
+ return 0;
+ }
+ localvp.attribute = da->attr;
+ localvp.lvalue = packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
+ break;
+
+ case PW_PACKET_SRC_PORT:
+ localvp.attribute = da->attr;
+ localvp.lvalue = packet->src_port;
+ break;
+
+ case PW_PACKET_DST_PORT:
+ localvp.attribute = da->attr;
+ localvp.lvalue = packet->dst_port;
+ break;
+
+ case PW_PACKET_AUTHENTICATION_VECTOR:
+ localvp.attribute = da->attr;
+ memcpy(localvp.vp_strvalue, packet->vector,
+ sizeof(packet->vector));
+ localvp.length = sizeof(packet->vector);
+ break;
+
+ /*
+ * Authorization, accounting, etc.
+ */
+ case PW_REQUEST_PROCESSING_STAGE:
+ if (request->component) {
+ strlcpy(out, request->component, outlen);
+ } else {
+ strlcpy(out, "server_core", outlen);
+ }
+ return strlen(out);
+
+ case PW_PACKET_SRC_IPV6_ADDRESS:
+ if (packet->src_ipaddr.af != AF_INET6) {
+ return 0;
+ }
+ localvp.attribute = da->attr;
+ memcpy(localvp.vp_strvalue,
+ &packet->src_ipaddr.ipaddr.ip6addr,
+ sizeof(packet->src_ipaddr.ipaddr.ip6addr));
+ break;
+
+ case PW_PACKET_DST_IPV6_ADDRESS:
+ if (packet->dst_ipaddr.af != AF_INET6) {
+ return 0;
+ }
+ localvp.attribute = da->attr;
+ memcpy(localvp.vp_strvalue,
+ &packet->dst_ipaddr.ipaddr.ip6addr,
+ sizeof(packet->dst_ipaddr.ipaddr.ip6addr));
+ break;
+
+ case PW_SERVER_IDENTITY:
+ if (!request->listener || !request->listener->identity) return 0;
+
+ snprintf(out, outlen, "%s", request->listener->identity);
+ return strlen(out);
+ break;
+
+ default:
+ return 0; /* not found */
+ break;
+ }
+
+ localvp.type = da->type;
+ return valuepair2str(out, outlen, &localvp,
+ da->type, func);
+ }
+
+ /*
+ * Not found, die.
+ */
+ return 0;
+ }
+
+ if (!vps) return 0; /* silently fail */
+
+ /*
+ * Convert the VP to a string, and return it.
+ */
+ return valuepair2str(out, outlen, vp, da->type, func);
}
+#ifdef HAVE_REGEX_H
/*
- * find the appropriate registered xlat function.
+ * Pull %{0} to %{8} out of the packet.
*/
-static struct xlat_cmp *find_xlat_func(const char *module)
+static int xlat_regex(void *instance, REQUEST *request,
+ char *fmt, char *out, size_t outlen,
+ RADIUS_ESCAPE_STRING func)
{
- struct xlat_cmp *c;
+ char *regex;
- for (c = cmp; c; c = c->next){
- if (strncmp(c->module,module,c->length) == 0 && *(module+c->length) == ':')
- break;
+ /*
+ * We cheat: fmt is "0" to "8", but those numbers
+ * are already in the "instance".
+ */
+ fmt = fmt; /* -Wunused */
+ func = func; /* -Wunused FIXME: do escaping? */
+
+ regex = request_data_reference(request, request,
+ REQUEST_DATA_REGEX | *(int *)instance);
+ if (!regex) return 0;
+
+ /*
+ * Copy UP TO "freespace" bytes, including
+ * a zero byte.
+ */
+ strlcpy(out, regex, outlen);
+ return strlen(out);
+}
+#endif /* HAVE_REGEX_H */
+
+/*
+ * Compare two xlat_t structs, based ONLY on the module name.
+ */
+static int xlat_cmp(const void *a, const void *b)
+{
+ if (((const xlat_t *)a)->length != ((const xlat_t *)b)->length) {
+ return ((const xlat_t *)a)->length - ((const xlat_t *)b)->length;
}
- return c;
+ return memcmp(((const xlat_t *)a)->module,
+ ((const xlat_t *)b)->module,
+ ((const xlat_t *)a)->length);
}
/*
- Convert the value on a VALUE_PAIR to string
-*/
-static int valuepair2str(char * out,int outlen,VALUE_PAIR * pair,
- int type, RADIUS_ESCAPE_STRING func)
+ * find the appropriate registered xlat function.
+ */
+static const xlat_t *xlat_find(const char *module)
{
- char buffer[MAX_STRING_LEN * 4];
+ xlat_t my_xlat;
- if (pair != NULL) {
- vp_prints_value(buffer, sizeof(buffer), pair, 0);
- return func(out, outlen, buffer);
+ /*
+ * Look for dictionary attributes first.
+ */
+ if ((dict_attrbyname(module) != NULL) ||
+ (strchr(module, '[') != NULL)) {
+ static const xlat_t dict_xlat = {
+ "request",
+ 7,
+ &xlat_inst[1],
+ xlat_packet,
+ TRUE
+ };
+
+ return &dict_xlat;
}
- switch (type) {
- case PW_TYPE_STRING :
- strNcpy(out,"_",outlen);
- break;
- case PW_TYPE_INTEGER :
- strNcpy(out,"0",outlen);
- break;
- case PW_TYPE_IPADDR :
- strNcpy(out,"?.?.?.?",outlen);
- break;
- case PW_TYPE_DATE :
- strNcpy(out,"0",outlen);
- break;
- default :
- strNcpy(out,"unknown_type",outlen);
- }
- return strlen(out);
+ strlcpy(my_xlat.module, module, sizeof(my_xlat.module));
+ my_xlat.length = strlen(my_xlat.module);
+
+ return rbtree_finddata(xlat_root, &my_xlat);
}
+
/*
- * Decode an attribute name from a particular RADIUS_PACKET
- * into a string.
+ * Register an xlat function.
*/
-static int decode_attr_packet(const char *from, char **to, int freespace,
- RADIUS_PACKET *packet,
- RADIUS_ESCAPE_STRING func)
-
+int xlat_register(const char *module, RAD_XLAT_FUNC func, void *instance)
{
- DICT_ATTR *tmpda;
- VALUE_PAIR *vp;
-
- tmpda = dict_attrbyname(from);
- if (!tmpda) return 0;
+ xlat_t *c;
+ xlat_t my_xlat;
+
+ if ((module == NULL) || (strlen(module) == 0)) {
+ DEBUG("xlat_register: Invalid module name");
+ return -1;
+ }
/*
- * See if the VP is defined.
+ * First time around, build up the tree...
+ *
+ * FIXME: This code should be hoisted out of this function,
+ * and into a global "initialization". But it isn't critical...
*/
- vp = pairfind(packet->vps, tmpda->attr);
- if (vp) {
- *to += valuepair2str(*to, freespace, vp,
- tmpda->type, func);
- return 1;
+ if (!xlat_root) {
+ int i;
+#ifdef HAVE_REGEX_H
+ char buffer[2];
+#endif
+
+ xlat_root = rbtree_create(xlat_cmp, free, 0);
+ if (!xlat_root) {
+ DEBUG("xlat_register: Failed to create tree.");
+ return -1;
+ }
+
+ /*
+ * Register the internal packet xlat's.
+ */
+ for (i = 0; internal_xlat[i] != NULL; i++) {
+ xlat_register(internal_xlat[i], xlat_packet, &xlat_inst[i]);
+ c = xlat_find(internal_xlat[i]);
+ rad_assert(c != NULL);
+ c->internal = TRUE;
+ }
+
+#ifdef HAVE_REGEX_H
+ /*
+ * Register xlat's for regexes.
+ */
+ buffer[1] = '\0';
+ for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
+ buffer[0] = '0' + i;
+ xlat_register(buffer, xlat_regex, &xlat_inst[i]);
+ c = xlat_find(buffer);
+ rad_assert(c != NULL);
+ c->internal = TRUE;
+ }
+#endif /* HAVE_REGEX_H */
}
/*
- * Non-protocol attributes.
+ * If it already exists, replace the instance.
*/
- switch (tmpda->attr) {
- case PW_PACKET_TYPE:
- {
- DICT_VALUE *dval;
-
- dval = dict_valbyattr(tmpda->attr, packet->code);
- if (dval) {
- snprintf(*to, freespace, "%s", dval->name);
- } else {
- snprintf(*to, freespace, "%d", packet->code);
- }
- *to += strlen(*to);
- return 1;
+ strlcpy(my_xlat.module, module, sizeof(my_xlat.module));
+ my_xlat.length = strlen(my_xlat.module);
+ c = rbtree_finddata(xlat_root, &my_xlat);
+ if (c) {
+ if (c->internal) {
+ DEBUG("xlat_register: Cannot re-define internal xlat");
+ return -1;
}
- break;
- default:
- break;
+ c->do_xlat = func;
+ c->instance = instance;
+ return 0;
}
+ /*
+ * Doesn't exist. Create it.
+ */
+ c = rad_malloc(sizeof(xlat_t));
+ memset(c, 0, sizeof(*c));
+
+ c->do_xlat = func;
+ strlcpy(c->module, module, sizeof(c->module));
+ c->length = strlen(c->module);
+ c->instance = instance;
+
+ rbtree_insert(xlat_root, c);
+
return 0;
}
/*
- * Decode an attribute name from a particular VALUE_PAIR*
- * into a string.
+ * Unregister an xlat function.
+ *
+ * We can only have one function to call per name, so the
+ * passing of "func" here is extraneous.
*/
-static int decode_attr_vps(const char *from, char **to, int freespace,
- VALUE_PAIR *vps,
- RADIUS_ESCAPE_STRING func)
-
+void xlat_unregister(const char *module, RAD_XLAT_FUNC func)
{
- DICT_ATTR *tmpda;
- VALUE_PAIR *vp;
-
- tmpda = dict_attrbyname(from);
- if (!tmpda) return 0;
+ rbnode_t *node;
+ xlat_t my_xlat;
- /*
- * See if the VP is defined.
- */
- vp = pairfind(vps, tmpda->attr);
- if (vp) {
- *to += valuepair2str(*to, freespace, vp,
- tmpda->type, func);
- return 1;
- }
+ func = func; /* -Wunused */
- return 0;
+ if (!module) return;
+
+ strlcpy(my_xlat.module, module, sizeof(my_xlat.module));
+ my_xlat.length = strlen(my_xlat.module);
+
+ node = rbtree_find(xlat_root, &my_xlat);
+ if (!node) return;
+
+ rbtree_delete(xlat_root, node);
+}
+
+/*
+ * De-register all xlat functions,
+ * used mainly for debugging.
+ */
+void xlat_free(void)
+{
+ rbtree_free(xlat_root);
}
+
/*
- * Decode an attribute name into a string.
+ * Decode an attribute name into a string.
*/
static void decode_attribute(const char **from, char **to, int freespace,
int *open, REQUEST *request,
RADIUS_ESCAPE_STRING func)
{
- char attrname[256];
+ int do_length = 0;
+ char xlat_name[128];
+ char *xlat_string = NULL; /* can be large */
+ int free_xlat_string = FALSE;
const char *p;
char *q, *pa;
- int stop=0, found=0;
+ int found=0, retlen=0;
int openbraces = *open;
- struct xlat_cmp *c;
+ const xlat_t *c;
p = *from;
q = *to;
- pa = &attrname[0];
+ pa = &xlat_name[0];
*q = '\0';
- /*
- * Skip the '{' at the front of 'p'
- * Increment open braces
- */
+ /*
+ * Skip the '{' at the front of 'p'
+ * Increment open braces
+ */
p++;
openbraces++;
+ if (*p == '#') {
+ p++;
+ do_length = 1;
+ }
+
/*
- * Copy over the rest of the string.
+ * First, copy the xlat key name to one buffer
*/
- while ((*p) && (!stop)) {
- switch(*p) {
+ while (*p && (*p != '}') && (*p != ':')) {
+ *pa++ = *p++;
+
+ if (pa >= (xlat_name + sizeof(xlat_name) - 1)) {
/*
- * Allow braces inside things, too.
+ * Skip to the end of the input
*/
+ p += strlen(p);
+ DEBUG("xlat: Module name is too long in string %%%s",
+ *from);
+ goto done;
+ }
+ }
+ *pa = '\0';
+
+ if (!*p) {
+ DEBUG("xlat: Invalid syntax in %s", *from);
+
+ /*
+ * %{name} is a simple attribute reference,
+ * or regex reference.
+ */
+ } else if (*p == '}') {
+ openbraces--;
+ rad_assert(openbraces == *open);
+
+ p++;
+ xlat_string = xlat_name;
+ goto do_xlat;
+
+ } else if (p[1] == '-') { /* handle ':- */
+ p += 2;
+ xlat_string = xlat_name;
+ goto do_xlat;
+
+ } else { /* module name, followed by per-module string */
+ int stop = 0;
+ int delimitbrace = *open;
+
+ rad_assert(*p == ':');
+ p++; /* skip the ':' */
+
+ /*
+ * If there's a brace immediately following the colon,
+ * then we've chosen to delimite the per-module string,
+ * so keep track of that.
+ */
+ if (*p == '{') {
+ delimitbrace = openbraces;
+ openbraces++;
+ p++;
+ }
+
+ xlat_string = rad_malloc(strlen(p) + 1); /* always returns */
+ free_xlat_string = TRUE;
+ pa = xlat_string;
+
+ /*
+ * Copy over the rest of the string, which is per-module
+ * data.
+ */
+ while (*p && !stop) {
+ switch(*p) {
+ /*
+ * What the heck is this supposed
+ * to be doing?
+ */
case '\\':
p++; /* skip it */
*pa++ = *p++;
break;
+ /*
+ * This is pretty hokey... we
+ * should use the functions in
+ * util.c
+ */
case '{':
openbraces++;
*pa++ = *p++;
break;
-
+
case '}':
openbraces--;
- if (openbraces == *open) {
+ if (openbraces == delimitbrace) {
p++;
stop=1;
} else {
*pa++ = *p++;
}
break;
-
- /*
- * Attr-Name1:-Attr-Name2
- *
- * Use Attr-Name1, and if not found,
- * use Attr-Name2.
- */
- case ':':
- if (p[1] == '-') {
- p += 2;
- stop = 1;
- break;
- }
- /* else FALL-THROUGH */
-
+
default:
*pa++ = *p++;
break;
+ }
}
- }
- *pa = '\0';
-
- /*
- * Find an attribute from the reply.
- */
- if (strncasecmp(attrname,"reply:",6) == 0) {
- found = decode_attr_packet(&attrname[6], &q, freespace,
- request->reply, func);
-
- /*
- * Find an attribute from the request.
- */
- } else if (strncasecmp(attrname,"request:",8) == 0) {
- found = decode_attr_packet(&attrname[8], &q, freespace,
- request->packet, func);
-
- /*
- * Find an attribute from the config items.
- */
- } else if (strncasecmp(attrname,"check:",6) == 0) {
- found = decode_attr_vps(&attrname[6], &q, freespace,
- request->config_items, func);
-
- /*
- * Find an attribute from the proxy request.
- */
- } else if ((strncasecmp(attrname,"proxy-request:",14) == 0) &&
- (request->proxy_reply != NULL)) {
- found = decode_attr_packet(&attrname[14], &q, freespace,
- request->proxy, func);
-
- /*
- * Find an attribute from the proxy reply.
- */
- } else if ((strncasecmp(attrname,"proxy-reply:",12) == 0) &&
- (request->proxy_reply != NULL)) {
- found = decode_attr_packet(&attrname[12], &q, freespace,
- request->proxy_reply, func);
- /*
- * Find a string from a registered function.
- */
- } else if ((c = find_xlat_func(attrname)) != NULL) {
- DEBUG("radius_xlat: Running registered xlat function of module %s for string \'%s\'",
- c->module, attrname+ c->length + 1);
- q += c->do_xlat(c->instance, request, attrname+(c->length+1), q, freespace, func);
- found = 1;
+ *pa = '\0';
/*
- * Nothing else, it MUST be a bare attribute name.
+ * Now check to see if we're at the end of the string
+ * we were sent. If we're not, check for :-
*/
- } else if (decode_attr_packet(attrname, &q, freespace, request->packet, func)) {
- found = 1;
-
- } else if (dict_attrbyname(attrname) == NULL) {
+ if (openbraces == delimitbrace) {
+ if (p[0] == ':' && p[1] == '-') {
+ p += 2;
+ }
+ }
+
/*
- * No attribute by that name, return an error.
+ * Look up almost everything in the new tree of xlat
+ * functions. This makes it a little quicker...
*/
- DEBUG2("WARNING: Attempt to use unknown xlat function, or non-existent attribute in string %%{%s}", attrname);
-
- } /* else the attribute is known, but not in the request */
+ do_xlat:
+ if ((c = xlat_find(xlat_name)) != NULL) {
+ if (!c->internal) DEBUG("radius_xlat: Running registered xlat function of module %s for string \'%s\'",
+ c->module, xlat_string);
+ retlen = c->do_xlat(c->instance, request, xlat_string,
+ q, freespace, func);
+ /* If retlen is 0, treat it as not found */
+ if (retlen > 0) found = 1;
+#ifndef NDEBUG
+ } else {
+ /*
+ * No attribute by that name, return an error.
+ */
+ DEBUG2("WARNING: Unknown module \"%s\" in string expansion \"%%%s\"", xlat_name, *from);
+#endif
+ }
+ }
/*
* Skip to last '}' if attr is found
* useless if we found what we need
*/
if (found) {
- while((*p != '\0') && (openbraces > 0)) {
+ if (do_length) {
+ snprintf(q, freespace, "%d", retlen);
+ retlen = strlen(q);
+ }
+
+ q += retlen;
+
+ while((*p != '\0') && (openbraces > *open)) {
+ /*
+ * Handle escapes outside of the loop.
+ */
+ if (*p == '\\') {
+ p++;
+ if (!*p) break;
+ p++; /* get & ignore next character */
+ continue;
+ }
+
switch (*p) {
default:
break;
/*
- * Ensure that escaped braces are allowed.
- */
- case '\\':
- p++; /* skip the escaped character */
- break;
-
- /*
* Bare brace
*/
case '{':
p++; /* skip the character */
}
}
+
+ done:
+ if (free_xlat_string) free(xlat_string);
*open = openbraces;
*from = p;
*/
static int xlat_copy(char *out, int outlen, const char *in)
{
- int len = 0;
+ int freespace = outlen;
- while (*in) {
- /*
- * Truncate, if too much.
- */
- if (len >= outlen) {
- break;
- }
+ rad_assert(outlen > 0);
+ while ((*in) && (freespace > 1)) {
/*
* Copy data.
*
* FIXME: Do escaping of bad stuff!
*/
- *out = *in;
+ *(out++) = *(in++);
- out++;
- in++;
- len++;
+ freespace--;
}
-
*out = '\0';
- return len;
+
+ return (outlen - freespace); /* count does not include NUL */
}
/*
int radius_xlat(char *out, int outlen, const char *fmt,
REQUEST *request, RADIUS_ESCAPE_STRING func)
{
- int i, c,freespace;
+ int c, len, freespace;
const char *p;
char *q;
+ char *nl;
VALUE_PAIR *tmp;
struct tm *TM, s_TM;
char tmpdt[40]; /* For temporary storing of dates */
int openbraces=0;
/*
+ * Catch bad modules.
+ */
+ if (!fmt || !out || !request) return 0;
+
+ /*
* Ensure that we always have an escaping function.
*/
if (func == NULL) {
func = xlat_copy;
}
- q = out;
+ q = out;
p = fmt;
while (*p) {
/* Calculate freespace in output */
/*
* We check if we're inside an open brace. If we are
* then we assume this brace is NOT literal, but is
- * a closing brace and apply it
+ * a closing brace and apply it
*/
- if((c == '}') && openbraces) {
+ if ((c == '}') && openbraces) {
openbraces--;
p++; /* skip it */
continue;
continue;
}
- if (*++p == '\0') break;
+ /*
+ * There's nothing after this character, copy
+ * the last '%' or "$' or '\\' over to the output
+ * buffer, and exit.
+ */
+ if (*++p == '\0') {
+ *q++ = c;
+ break;
+ }
if (c == '\\') {
switch(*p) {
break;
}
p++;
+
+ /*
+ * Hmmm... ${User-Name} is a synonym for
+ * %{User-Name}.
+ *
+ * Why, exactly?
+ */
} else if (c == '$') switch(*p) {
case '{': /* Attribute by Name */
decode_attribute(&p, &q, freespace, &openbraces, request, func);
break;
case 'd': /* request day */
TM = localtime_r(&request->timestamp, &s_TM);
- strftime(tmpdt,sizeof(tmpdt),"%d",TM);
- strNcpy(q,tmpdt,freespace);
- q += strlen(q);
+ len = strftime(tmpdt, sizeof(tmpdt), "%d", TM);
+ if (len > 0) {
+ strlcpy(q, tmpdt, freespace);
+ q += strlen(q);
+ }
p++;
break;
case 'f': /* Framed IP address */
p++;
break;
case 'l': /* request timestamp */
- snprintf(tmpdt, sizeof(tmpdt), "%lu",request->timestamp);
- strNcpy(q,tmpdt,freespace);
+ snprintf(tmpdt, sizeof(tmpdt), "%lu",
+ (unsigned long) request->timestamp);
+ strlcpy(q,tmpdt,freespace);
q += strlen(q);
p++;
break;
case 'm': /* request month */
TM = localtime_r(&request->timestamp, &s_TM);
- strftime(tmpdt,sizeof(tmpdt),"%m",TM);
- strNcpy(q,tmpdt,freespace);
- q += strlen(q);
+ len = strftime(tmpdt, sizeof(tmpdt), "%m", TM);
+ if (len > 0) {
+ strlcpy(q, tmpdt, freespace);
+ q += strlen(q);
+ }
p++;
break;
case 'n': /* NAS IP address */
p++;
break;
case 't': /* request timestamp */
- ctime_r(&request->timestamp, q);
+ CTIME_R(&request->timestamp, tmpdt, sizeof(tmpdt));
+ nl = strchr(tmpdt, '\n');
+ if (nl) *nl = '\0';
+ strlcpy(q, tmpdt, freespace);
q += strlen(q);
p++;
break;
p++;
break;
case 'A': /* radacct_dir */
- strNcpy(q,radacct_dir,freespace-1);
+ strlcpy(q,radacct_dir,freespace);
q += strlen(q);
p++;
break;
case 'C': /* ClientName */
- strNcpy(q,client_name(request->packet->src_ipaddr),freespace-1);
+ strlcpy(q,client_name_old(&request->packet->src_ipaddr),freespace);
q += strlen(q);
p++;
break;
case 'D': /* request date */
TM = localtime_r(&request->timestamp, &s_TM);
- strftime(tmpdt,sizeof(tmpdt),"%Y%m%d",TM);
- strNcpy(q,tmpdt,freespace);
- q += strlen(q);
+ len = strftime(tmpdt, sizeof(tmpdt), "%Y%m%d", TM);
+ if (len > 0) {
+ strlcpy(q, tmpdt, freespace);
+ q += strlen(q);
+ }
p++;
break;
case 'H': /* request hour */
TM = localtime_r(&request->timestamp, &s_TM);
- strftime(tmpdt,sizeof(tmpdt),"%H",TM);
- strNcpy(q,tmpdt,freespace);
- q += strlen(q);
+ len = strftime(tmpdt, sizeof(tmpdt), "%H", TM);
+ if (len > 0) {
+ strlcpy(q, tmpdt, freespace);
+ q += strlen(q);
+ }
p++;
break;
case 'L': /* radlog_dir */
- strNcpy(q,radlog_dir,freespace-1);
+ strlcpy(q,radlog_dir,freespace);
q += strlen(q);
p++;
break;
p++;
break;
case 'R': /* radius_dir */
- strNcpy(q,radius_dir,freespace-1);
+ strlcpy(q,radius_dir,freespace);
q += strlen(q);
p++;
break;
case 'S': /* request timestamp in SQL format*/
TM = localtime_r(&request->timestamp, &s_TM);
- strftime(tmpdt,sizeof(tmpdt),"%Y-%m-%d %H:%M:%S",TM);
- strNcpy(q,tmpdt,freespace);
- q += strlen(q);
+ len = strftime(tmpdt, sizeof(tmpdt), "%Y-%m-%d %H:%M:%S", TM);
+ if (len > 0) {
+ strlcpy(q, tmpdt, freespace);
+ q += strlen(q);
+ }
p++;
break;
case 'T': /* request timestamp */
TM = localtime_r(&request->timestamp, &s_TM);
- strftime(tmpdt,sizeof(tmpdt),"%Y-%m-%d-%H.%M.%S.000000",TM);
- strNcpy(q,tmpdt,freespace);
- q += strlen(q);
+ len = strftime(tmpdt, sizeof(tmpdt), "%Y-%m-%d-%H.%M.%S.000000", TM);
+ if (len > 0) {
+ strlcpy(q, tmpdt, freespace);
+ q += strlen(q);
+ }
p++;
break;
case 'U': /* Stripped User name */
break;
case 'V': /* Request-Authenticator */
if (request->packet->verified)
- strNcpy(q,"Verified",freespace-1);
+ strlcpy(q,"Verified",freespace);
else
- strNcpy(q,"None",freespace-1);
+ strlcpy(q,"None",freespace);
q += strlen(q);
p++;
break;
case 'Y': /* request year */
TM = localtime_r(&request->timestamp, &s_TM);
- strftime(tmpdt,sizeof(tmpdt),"%Y",TM);
- strNcpy(q,tmpdt,freespace);
- q += strlen(q);
+ len = strftime(tmpdt, sizeof(tmpdt), "%Y", TM);
+ if (len > 0) {
+ strlcpy(q, tmpdt, freespace);
+ q += strlen(q);
+ }
p++;
break;
case 'Z': /* Full request pairs except password */
tmp = request->packet->vps;
while (tmp && (freespace > 3)) {
- if (tmp->attribute != PW_PASSWORD) {
+ if (tmp->attribute != PW_USER_PASSWORD) {
*q++ = '\t';
- i = vp_prints(q,freespace-2,tmp);
- q += i;
- freespace -= (i+2);
+ len = vp_prints(q, freespace - 2, tmp);
+ q += len;
+ freespace -= (len + 2);
*q++ = '\n';
}
tmp = tmp->next;