Fix issues found by LLVM checker.
[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, int *answer)
69 {
70         int             i, found;
71         uint32_t        result;
72         int             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, result;
224         rlm_expr_t      *inst = instance;
225         const           char *p;
226         char            buffer[256];
227
228         inst = inst;            /* -Wunused */
229
230         /*
231          * Do an xlat on the provided string (nice recursive operation).
232          */
233         if (!radius_xlat(buffer, sizeof(buffer), fmt, request, func)) {
234                 radlog(L_ERR, "rlm_expr: xlat failed.");
235                 return 0;
236         }
237
238         p = buffer;
239         rcode = get_number(request, &p, &result);
240         if (rcode < 0) {
241                 return 0;
242         }
243
244         /*
245          *  We MUST have eaten the entire input string.
246          */
247         if (*p != '\0') {
248                 RDEBUG2("Failed at %s", p);
249                 return 0;
250         }
251
252         snprintf(out, outlen, "%d", result);
253         return strlen(out);
254 }
255
256 /*
257  *      Do any per-module initialization that is separate to each
258  *      configured instance of the module.  e.g. set up connections
259  *      to external databases, read configuration files, set up
260  *      dictionary entries, etc.
261  *
262  *      If configuration information is given in the config section
263  *      that must be referenced in later calls, store a handle to it
264  *      in *instance otherwise put a null pointer there.
265  */
266 static int expr_instantiate(CONF_SECTION *conf, void **instance)
267 {
268         rlm_expr_t      *inst;
269         const char      *xlat_name;
270
271         /*
272          *      Set up a storage area for instance data
273          */
274
275         inst = rad_malloc(sizeof(rlm_expr_t));
276         if (!inst)
277                 return -1;
278         memset(inst, 0, sizeof(rlm_expr_t));
279
280         xlat_name = cf_section_name2(conf);
281         if (xlat_name == NULL)
282                 xlat_name = cf_section_name1(conf);
283         if (xlat_name){
284                 inst->xlat_name = strdup(xlat_name);
285                 xlat_register(xlat_name, expr_xlat, inst);
286         }
287         /*
288          * Initialize various paircompare functions
289          */
290         pair_builtincompare_init();
291         *instance = inst;
292
293         return 0;
294 }
295
296 /*
297  * Detach a instance free all ..
298  */
299 static int expr_detach(void *instance)
300 {
301         rlm_expr_t      *inst = instance;
302
303         xlat_unregister(inst->xlat_name, expr_xlat);
304         pair_builtincompare_detach();
305         free(inst->xlat_name);
306
307         free(inst);
308         return 0;
309 }
310
311 /*
312  *      The module name should be the only globally exported symbol.
313  *      That is, everything else should be 'static'.
314  *
315  *      If the module needs to temporarily modify it's instantiation
316  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
317  *      The server will then take care of ensuring that the module
318  *      is single-threaded.
319  */
320 module_t rlm_expr = {
321         RLM_MODULE_INIT,
322         "expr",                         /* Name */
323         RLM_TYPE_CHECK_CONFIG_SAFE,     /* type */
324         expr_instantiate,               /* instantiation */
325         expr_detach,                    /* detach */
326         {
327                 NULL,                   /* authentication */
328                 NULL,                   /* authorization */
329                 NULL,                   /* pre-accounting */
330                 NULL                    /* accounting */
331         },
332 };