rlm_eap: add eap_chbind.c to build
[freeradius.git] / src / modules / rlm_expr / rlm_expr.c
1 /*
2  * rlm_expr.c
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2002,2006  The FreeRADIUS server project
21  * Copyright 2002  Alan DeKok <aland@ox.org>
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
29 #include "rlm_expr.h"
30
31 /*
32  *      Define a structure for our module configuration.
33  */
34 typedef struct rlm_expr_t {
35         char *xlat_name;
36 } rlm_expr_t;
37
38 typedef enum expr_token_t {
39   TOKEN_NONE = 0,
40   TOKEN_INTEGER,
41   TOKEN_ADD,
42   TOKEN_SUBTRACT,
43   TOKEN_DIVIDE,
44   TOKEN_REMAINDER,
45   TOKEN_MULTIPLY,
46   TOKEN_AND,
47   TOKEN_OR,
48   TOKEN_LAST
49 } expr_token_t;
50
51 typedef struct expr_map_t {
52         char op;
53         expr_token_t token;
54 } expr_map_t;
55
56 static expr_map_t map[] =
57 {
58         {'+',   TOKEN_ADD },
59         {'-',   TOKEN_SUBTRACT },
60         {'/',   TOKEN_DIVIDE },
61         {'*',   TOKEN_MULTIPLY },
62         {'%',   TOKEN_REMAINDER },
63         {'&',   TOKEN_AND },
64         {'|',   TOKEN_OR },
65         {0,     TOKEN_LAST}
66 };
67
68 static int get_number(REQUEST *request, const char **string, int64_t *answer)
69 {
70         int             i, found;
71         int64_t         result;
72         int64_t         x;
73         const char      *p;
74         expr_token_t    this;
75
76         /*
77          *  Loop over the input.
78          */
79         result = 0;
80         this = TOKEN_NONE;
81
82         for (p = *string; *p != '\0'; /* nothing */) {
83                 if ((*p == ' ') ||
84                     (*p == '\t')) {
85                         p++;
86                         continue;
87                 }
88
89                 /*
90                  *  Discover which token it is.
91                  */
92                 found = FALSE;
93                 for (i = 0; map[i].token != TOKEN_LAST; i++) {
94                         if (*p == map[i].op) {
95                                 if (this != TOKEN_NONE) {
96                                         RDEBUG2("Invalid operator at \"%s\"", p);
97                                         return -1;
98                                 }
99                                 this = map[i].token;
100                                 p++;
101                                 found = TRUE;
102                                 break;
103                         }
104                 }
105
106                 /*
107                  *  Found the algebraic operator.  Get the next number.
108                  */
109                 if (found) {
110                         continue;
111                 }
112
113                 /*
114                  *  End of a group.  Stop.
115                  */
116                 if (*p == ')') {
117                         if (this != TOKEN_NONE) {
118                                 RDEBUG2("Trailing operator before end sub-expression at \"%s\"", p);
119                                 return -1;
120                         }
121                         p++;
122                         break;
123                 }
124
125                 /*
126                  *  Start of a group.  Call ourselves recursively.
127                  */
128                 if (*p == '(') {
129                         p++;
130
131                         found = get_number(request, &p, &x);
132                         if (found < 0) {
133                                 return -1;
134                         }
135                 } else {
136                         /*
137                          *  No algrebraic operator found, the next thing
138                          *  MUST be a number.
139                          *
140                          *  If it isn't, then we die.
141                          */
142                         if ((*p < '0') || (*p > '9')) {
143                                 RDEBUG2("Not a number at \"%s\"", p);
144                                 return -1;
145                         }
146
147                         /*
148                          *  This is doing it the hard way, but it also allows
149                          *  us to increment 'p'.
150                          */
151                         x = 0;
152                         while ((*p >= '0') && (*p <= '9')) {
153                                 x *= 10;
154                                 x += (*p - '0');
155                                 p++;
156                         }
157                 }
158
159                 switch (this) {
160                 default:
161                 case TOKEN_NONE:
162                         result = x;
163                         break;
164
165                 case TOKEN_ADD:
166                         result += x;
167                         break;
168
169                 case TOKEN_SUBTRACT:
170                         result -= x;
171                         break;
172
173                 case TOKEN_DIVIDE:
174                         if (x == 0) {
175                                 result = 0; /* we don't have NaN for integers */
176                                 break;
177                         }
178                         result /= x;
179                         break;
180
181                 case TOKEN_REMAINDER:
182                         if (x == 0) {
183                                 result = 0; /* we don't have NaN for integers */
184                                 break;
185                         }
186                         result %= x;
187                         break;
188
189                 case TOKEN_MULTIPLY:
190                         result *= x;
191                         break;
192
193                 case TOKEN_AND:
194                         result &= x;
195                         break;
196
197                 case TOKEN_OR:
198                         result |= x;
199                         break;
200                 }
201
202                 /*
203                  *  We've used this token.
204                  */
205                 this = TOKEN_NONE;
206         }
207
208         /*
209          *  And return the answer to the caller.
210          */
211         *string = p;
212         *answer = result;
213         return 0;
214 }
215
216 /*
217  *  Do xlat of strings!
218  */
219 static size_t expr_xlat(void *instance, REQUEST *request, char *fmt,
220                         char *out, size_t outlen,
221                      RADIUS_ESCAPE_STRING func)
222 {
223         int             rcode;
224         int64_t         result;
225         rlm_expr_t      *inst = instance;
226         const           char *p;
227         char            buffer[256];
228
229         inst = inst;            /* -Wunused */
230
231         /*
232          * Do an xlat on the provided string (nice recursive operation).
233          */
234         if (!radius_xlat(buffer, sizeof(buffer), fmt, request, func)) {
235                 radlog(L_ERR, "rlm_expr: xlat failed.");
236                 return 0;
237         }
238
239         p = buffer;
240         rcode = get_number(request, &p, &result);
241         if (rcode < 0) {
242                 return 0;
243         }
244
245         /*
246          *  We MUST have eaten the entire input string.
247          */
248         if (*p != '\0') {
249                 RDEBUG2("Failed at %s", p);
250                 return 0;
251         }
252
253         snprintf(out, outlen, "%ld", (long int) result);
254         return strlen(out);
255 }
256
257 static size_t rand_xlat(void *instance, REQUEST *request, char *fmt,
258                         char *out, size_t outlen,
259                         RADIUS_ESCAPE_STRING func)
260 {
261         int             rcode;
262         int64_t         result;
263         rlm_expr_t      *inst = instance;
264         char            buffer[256];
265
266         inst = inst;            /* -Wunused */
267
268         /*
269          * Do an xlat on the provided string (nice recursive operation).
270          */
271         if (!radius_xlat(buffer, sizeof(buffer), fmt, request, func)) {
272                 radlog(L_ERR, "rlm_expr: xlat failed.");
273                 return 0;
274         }
275
276         result = atoi(buffer);
277
278         /*
279          *      Too small or too big.
280          */
281         if (result <= 0) return 0;
282         if (result >= (1 << 30)) result = (1 << 30);
283
284         result *= fr_rand();    /* 0..2^32-1 */
285         result >>= 32;
286
287         snprintf(out, outlen, "%ld", (long int) result);
288         return strlen(out);
289 }
290
291 /*
292  *      Do any per-module initialization that is separate to each
293  *      configured instance of the module.  e.g. set up connections
294  *      to external databases, read configuration files, set up
295  *      dictionary entries, etc.
296  *
297  *      If configuration information is given in the config section
298  *      that must be referenced in later calls, store a handle to it
299  *      in *instance otherwise put a null pointer there.
300  */
301 static int expr_instantiate(CONF_SECTION *conf, void **instance)
302 {
303         rlm_expr_t      *inst;
304         const char      *xlat_name;
305
306         /*
307          *      Set up a storage area for instance data
308          */
309
310         inst = rad_malloc(sizeof(rlm_expr_t));
311         if (!inst)
312                 return -1;
313         memset(inst, 0, sizeof(rlm_expr_t));
314
315         xlat_name = cf_section_name2(conf);
316         if (xlat_name == NULL)
317                 xlat_name = cf_section_name1(conf);
318         if (xlat_name){
319                 inst->xlat_name = strdup(xlat_name);
320                 xlat_register(xlat_name, expr_xlat, inst);
321         }
322
323         xlat_register("rand", rand_xlat, inst);
324
325         /*
326          * Initialize various paircompare functions
327          */
328         pair_builtincompare_init();
329         *instance = inst;
330
331         return 0;
332 }
333
334 /*
335  * Detach a instance free all ..
336  */
337 static int expr_detach(void *instance)
338 {
339         rlm_expr_t      *inst = instance;
340
341         xlat_unregister(inst->xlat_name, expr_xlat);
342         pair_builtincompare_detach();
343         free(inst->xlat_name);
344
345         free(inst);
346         return 0;
347 }
348
349 /*
350  *      The module name should be the only globally exported symbol.
351  *      That is, everything else should be 'static'.
352  *
353  *      If the module needs to temporarily modify it's instantiation
354  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
355  *      The server will then take care of ensuring that the module
356  *      is single-threaded.
357  */
358 module_t rlm_expr = {
359         RLM_MODULE_INIT,
360         "expr",                         /* Name */
361         RLM_TYPE_CHECK_CONFIG_SAFE,     /* type */
362         expr_instantiate,               /* instantiation */
363         expr_detach,                    /* detach */
364         {
365                 NULL,                   /* authentication */
366                 NULL,                   /* authorization */
367                 NULL,                   /* pre-accounting */
368                 NULL                    /* accounting */
369         },
370 };