#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modules.h>
#include "rlm_expr.h"
typedef struct rlm_expr_t {
char *xlat_name;
} rlm_expr_t;
typedef enum expr_token_t {
TOKEN_NONE = 0,
TOKEN_INTEGER,
TOKEN_ADD,
TOKEN_SUBTRACT,
TOKEN_DIVIDE,
TOKEN_REMAINDER,
TOKEN_MULTIPLY,
TOKEN_AND,
TOKEN_OR,
TOKEN_LAST
} expr_token_t;
typedef struct expr_map_t {
char op;
expr_token_t token;
} expr_map_t;
static expr_map_t map[] =
{
{'+', TOKEN_ADD },
{'-', TOKEN_SUBTRACT },
{'/', TOKEN_DIVIDE },
{'*', TOKEN_MULTIPLY },
{'%', TOKEN_REMAINDER },
{'&', TOKEN_AND },
{'|', TOKEN_OR },
{0, TOKEN_LAST}
};
static int get_number(REQUEST *request, const char **string, int64_t *answer)
{
int i, found;
int64_t result;
int64_t x;
const char *p;
expr_token_t this;
result = 0;
this = TOKEN_NONE;
for (p = *string; *p != '\0'; ) {
if ((*p == ' ') ||
(*p == '\t')) {
p++;
continue;
}
found = FALSE;
for (i = 0; map[i].token != TOKEN_LAST; i++) {
if (*p == map[i].op) {
if (this != TOKEN_NONE) {
RDEBUG2("Invalid operator at \"%s\"", p);
return -1;
}
this = map[i].token;
p++;
found = TRUE;
break;
}
}
if (found) {
continue;
}
if (*p == ')') {
if (this != TOKEN_NONE) {
RDEBUG2("Trailing operator before end sub-expression at \"%s\"", p);
return -1;
}
p++;
break;
}
if (*p == '(') {
p++;
found = get_number(request, &p, &x);
if (found < 0) {
return -1;
}
} else {
if ((*p < '0') || (*p > '9')) {
RDEBUG2("Not a number at \"%s\"", p);
return -1;
}
x = 0;
while ((*p >= '0') && (*p <= '9')) {
x *= 10;
x += (*p - '0');
p++;
}
}
switch (this) {
default:
case TOKEN_NONE:
result = x;
break;
case TOKEN_ADD:
result += x;
break;
case TOKEN_SUBTRACT:
result -= x;
break;
case TOKEN_DIVIDE:
if (x == 0) {
result = 0;
break;
}
result /= x;
break;
case TOKEN_REMAINDER:
if (x == 0) {
result = 0;
break;
}
result %= x;
break;
case TOKEN_MULTIPLY:
result *= x;
break;
case TOKEN_AND:
result &= x;
break;
case TOKEN_OR:
result |= x;
break;
}
this = TOKEN_NONE;
}
*string = p;
*answer = result;
return 0;
}
static size_t expr_xlat(void *instance, REQUEST *request, char *fmt,
char *out, size_t outlen,
RADIUS_ESCAPE_STRING func)
{
int rcode;
int64_t result;
rlm_expr_t *inst = instance;
const char *p;
char buffer[256];
inst = inst;
if (!radius_xlat(buffer, sizeof(buffer), fmt, request, func)) {
radlog(L_ERR, "rlm_expr: xlat failed.");
return 0;
}
p = buffer;
rcode = get_number(request, &p, &result);
if (rcode < 0) {
return 0;
}
if (*p != '\0') {
RDEBUG2("Failed at %s", p);
return 0;
}
snprintf(out, outlen, "%ld", (long int) result);
return strlen(out);
}
static size_t rand_xlat(void *instance, REQUEST *request, char *fmt,
char *out, size_t outlen,
RADIUS_ESCAPE_STRING func)
{
int rcode;
int64_t result;
rlm_expr_t *inst = instance;
char buffer[256];
inst = inst;
if (!radius_xlat(buffer, sizeof(buffer), fmt, request, func)) {
radlog(L_ERR, "rlm_expr: xlat failed.");
return 0;
}
result = atoi(buffer);
if (result <= 0) return 0;
if (result >= (1 << 30)) result = (1 << 30);
result *= fr_rand();
result >>= 32;
snprintf(out, outlen, "%ld", (long int) result);
return strlen(out);
}
static int expr_instantiate(CONF_SECTION *conf, void **instance)
{
rlm_expr_t *inst;
const char *xlat_name;
inst = rad_malloc(sizeof(rlm_expr_t));
if (!inst)
return -1;
memset(inst, 0, sizeof(rlm_expr_t));
xlat_name = cf_section_name2(conf);
if (xlat_name == NULL)
xlat_name = cf_section_name1(conf);
if (xlat_name){
inst->xlat_name = strdup(xlat_name);
xlat_register(xlat_name, expr_xlat, inst);
}
xlat_register("rand", rand_xlat, inst);
pair_builtincompare_init();
*instance = inst;
return 0;
}
static int expr_detach(void *instance)
{
rlm_expr_t *inst = instance;
xlat_unregister(inst->xlat_name, expr_xlat);
pair_builtincompare_detach();
free(inst->xlat_name);
free(inst);
return 0;
}
module_t rlm_expr = {
RLM_MODULE_INIT,
"expr",
RLM_TYPE_CHECK_CONFIG_SAFE,
expr_instantiate,
expr_detach,
{
NULL,
NULL,
NULL,
NULL
},
};