Fix argument order
[freeradius.git] / src / modules / rlm_sqlippool / rlm_sqlippool.c
1 /*
2  *   This program is is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License as published by
4  *   the Free Software Foundation; either version 2 of the License, or (at
5  *   your option) any later version.
6  *
7  *   This program is distributed in the hope that it will be useful,
8  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *   GNU General Public License for more details.
11  *
12  *   You should have received a copy of the GNU General Public License
13  *   along with this program; if not, write to the Free Software
14  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16
17 /**
18  * $Id$
19  * @file rlm_sqlippool.c
20  * @brief Allocates an IPv4 address from pools stored in SQL.
21  *
22  * @copyright 2002  Globe.Net Communications Limited
23  * @copyright 2006  The FreeRADIUS server project
24  * @copyright 2006  Suntel Communications
25  */
26 RCSID("$Id$")
27
28 #include <freeradius-devel/radiusd.h>
29 #include <freeradius-devel/rad_assert.h>
30
31 #include <ctype.h>
32
33 #include <rlm_sql.h>
34
35 #define MAX_QUERY_LEN 4096
36
37 /*
38  *      Define a structure for our module configuration.
39  */
40 typedef struct rlm_sqlippool_t {
41         char const      *sql_instance_name;
42
43         uint32_t        lease_duration;
44
45         rlm_sql_t       *sql_inst;
46
47         char const      *pool_name;
48         bool            ipv6;                   //!< Whether or not we do IPv6 pools.
49         int             framed_ip_address;      //!< the attribute number for Framed-IP(v6)-Address
50
51         time_t          last_clear;             //!< So we only do it once a second.
52         char const      *allocate_begin;        //!< SQL query to begin.
53         char const      *allocate_clear;        //!< SQL query to clear an IP.
54         char const      *allocate_find;         //!< SQL query to find an unused IP.
55         char const      *allocate_update;       //!< SQL query to mark an IP as used.
56         char const      *allocate_commit;       //!< SQL query to commit.
57
58         char const      *pool_check;            //!< Query to check for the existence of the pool.
59
60                                                 /* Start sequence */
61         char const      *start_begin;           //!< SQL query to begin.
62         char const      *start_update;          //!< SQL query to update an IP entry.
63         char const      *start_commit;          //!< SQL query to commit.
64
65                                                 /* Alive sequence */
66         char const      *alive_begin;           //!< SQL query to begin.
67         char const      *alive_update;          //!< SQL query to update an IP entry.
68         char const      *alive_commit;          //!< SQL query to commit.
69
70                                                 /* Stop sequence */
71         char const      *stop_begin;            //!< SQL query to begin.
72         char const      *stop_clear;            //!< SQL query to clear an IP.
73         char const      *stop_commit;           //!< SQL query to commit.
74
75                                                 /* On sequence */
76         char const      *on_begin;              //!< SQL query to begin.
77         char const      *on_clear;              //!< SQL query to clear an entire NAS.
78         char const      *on_commit;             //!< SQL query to commit.
79
80                                                 /* Off sequence */
81         char const      *off_begin;             //!< SQL query to begin.
82         char const      *off_clear;             //!< SQL query to clear an entire NAS.
83         char const      *off_commit;            //!< SQL query to commit.
84
85                                                 /* Logging Section */
86         char const      *log_exists;            //!< There was an ip address already assigned.
87         char const      *log_success;           //!< We successfully allocated ip address from pool.
88         char const      *log_clear;             //!< We successfully deallocated ip address from pool.
89         char const      *log_failed;            //!< Failed to allocate ip from the pool.
90         char const      *log_nopool;            //!< There was no Framed-IP-Address but also no Pool-Name.
91
92                                                 /* Reserved to handle 255.255.255.254 Requests */
93         char const      *defaultpool;           //!< Default Pool-Name if there is none in the check items.
94
95 } rlm_sqlippool_t;
96
97 static CONF_PARSER message_config[] = {
98         { "exists", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, log_exists), NULL },
99         { "success", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, log_success), NULL },
100         { "clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, log_clear), NULL },
101         { "failed", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, log_failed), NULL },
102         { "nopool", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, log_nopool), NULL },
103
104         { NULL, -1, 0, NULL, NULL }
105 };
106
107 /*
108  *      A mapping of configuration file names to internal variables.
109  *
110  *      Note that the string is dynamically allocated, so it MUST
111  *      be freed.  When the configuration file parse re-reads the string,
112  *      it free's the old one, and strdup's the new one, placing the pointer
113  *      to the strdup'd string into 'config.string'.  This gets around
114  *      buffer over-flows.
115  */
116 static CONF_PARSER module_config[] = {
117         { "sql-instance-name", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, sql_instance_name), NULL },
118         { "sql_module_instance", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_sqlippool_t, sql_instance_name), "sql" },
119
120         { "lease-duration", FR_CONF_OFFSET(PW_TYPE_INTEGER | PW_TYPE_DEPRECATED, rlm_sqlippool_t, lease_duration), NULL },
121         { "lease_duration", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_sqlippool_t, lease_duration), "86400" },
122
123         { "pool-name", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, pool_name), NULL },
124         { "pool_name", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, pool_name), "" },
125
126         { "default-pool", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, defaultpool), NULL },
127         { "default_pool", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, defaultpool), "main_pool" },
128
129
130         { "ipv6", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_sqlippool_t, ipv6), NULL},
131
132         { "allocate-begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, allocate_begin), NULL },
133         { "allocate_begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, allocate_begin), "START TRANSACTION" },
134
135         { "allocate-clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, allocate_clear), NULL },
136         { "allocate_clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_REQUIRED, rlm_sqlippool_t, allocate_clear), ""  },
137
138         { "allocate-find", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, allocate_find), NULL },
139         { "allocate_find", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_REQUIRED, rlm_sqlippool_t, allocate_find), ""  },
140
141         { "allocate-update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, allocate_update), NULL },
142         { "allocate_update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_REQUIRED, rlm_sqlippool_t, allocate_update), ""  },
143
144         { "allocate-commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, allocate_commit), NULL },
145         { "allocate_commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, allocate_commit), "COMMIT" },
146
147
148         { "pool-check", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, pool_check), NULL },
149         { "pool_check", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, pool_check), ""  },
150
151
152         { "start-begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, start_begin), NULL },
153         { "start_begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, start_begin), "START TRANSACTION" },
154
155         { "start-update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, start_update), NULL },
156         { "start_update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_REQUIRED, rlm_sqlippool_t, start_update), ""  },
157
158         { "start-commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, start_commit), NULL },
159         { "start_commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, start_commit), "COMMIT" },
160
161
162         { "alive-begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, alive_begin), NULL },
163         { "alive_begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, alive_begin), "START TRANSACTION" },
164
165         { "alive-update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, alive_update), NULL },
166         { "alive_update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_REQUIRED, rlm_sqlippool_t, alive_update), ""  },
167
168         { "alive-commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, alive_commit), NULL },
169         { "alive_commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, alive_commit), "COMMIT" },
170
171
172         { "stop-begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, stop_begin), NULL },
173         { "stop_begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, stop_begin), "START TRANSACTION" },
174
175         { "stop-clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, stop_clear), NULL },
176         { "stop_clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_REQUIRED, rlm_sqlippool_t, stop_clear), ""  },
177
178         { "stop-commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, stop_commit), NULL },
179         { "stop_commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, stop_commit), "COMMIT" },
180
181
182         { "on-begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, on_begin), NULL },
183         { "on_begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, on_begin), "START TRANSACTION" },
184
185         { "on-clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, on_clear), NULL },
186         { "on_clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_REQUIRED, rlm_sqlippool_t, on_clear), ""  },
187
188         { "on-commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, on_commit), NULL },
189         { "on_commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, on_commit), "COMMIT" },
190
191
192         { "off-begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, off_begin), NULL },
193         { "off_begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, off_begin), "START TRANSACTION" },
194
195         { "off-clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, off_clear), NULL },
196         { "off_clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_REQUIRED, rlm_sqlippool_t, off_clear), ""  },
197
198         { "off-commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, off_commit), NULL },
199         { "off_commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, off_commit), "COMMIT" },
200
201         { "messages", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) message_config },
202
203         { NULL, -1, 0, NULL, NULL }
204 };
205
206 /*
207  *      Replace %<whatever> in a string.
208  *
209  *      %P      pool_name
210  *      %I      param
211  *      %J      lease_duration
212  *
213  */
214 static int sqlippool_expand(char * out, int outlen, char const * fmt,
215                             rlm_sqlippool_t *data, char * param, int param_len)
216 {
217         char *q;
218         char const *p;
219         char tmp[40]; /* For temporary storing of integers */
220
221         q = out;
222         for (p = fmt; *p ; p++) {
223                 int freespace;
224                 int c;
225
226                 /* Calculate freespace in output */
227                 freespace = outlen - (q - out);
228                 if (freespace <= 1)
229                         break;
230
231                 c = *p;
232                 if (c != '%') {
233                         *q++ = *p;
234                         continue;
235                 }
236
237                 if (*++p == '\0') {
238                         break;
239                 }
240
241                 if (c == '%') {
242                         switch (*p) {
243                         case 'P': /* pool name */
244                                 strlcpy(q, data->pool_name, freespace);
245                                 q += strlen(q);
246                                 break;
247                         case 'I': /* IP address */
248                                 if (param && param_len > 0) {
249                                         if (param_len > freespace) {
250                                                 strlcpy(q, param, freespace);
251                                                 q += strlen(q);
252                                         }
253                                         else {
254                                                 memcpy(q, param, param_len);
255                                                 q += param_len;
256                                         }
257                                 }
258                                 break;
259                         case 'J': /* lease duration */
260                                 sprintf(tmp, "%d", data->lease_duration);
261                                 strlcpy(q, tmp, freespace);
262                                 q += strlen(q);
263                                 break;
264
265                         default:
266                                 *q++ = '%';
267                                 *q++ = *p;
268                                 break;
269                         }
270                 }
271         }
272         *q = '\0';
273
274 #if 0
275         DEBUG2("sqlippool_expand: \"%s\"", out);
276 #endif
277
278         return strlen(out);
279 }
280
281 /** Perform a single sqlippool query
282  *
283  * Mostly wrapper around sql_query which does some special sqlippool sequence substitutions and expands
284  * the format string.
285  *
286  * @param fmt sql query to expand.
287  * @param handle sql connection handle.
288  * @param data Instance of rlm_sqlippool.
289  * @param request Current request.
290  * @param param ip address string.
291  * @param param_len ip address string len.
292  * @return 0 on success or < 0 on error.
293  */
294 static int sqlippool_command(char const * fmt, rlm_sql_handle_t * handle, rlm_sqlippool_t *data, REQUEST *request,
295                              char *param, int param_len)
296 {
297         char query[MAX_QUERY_LEN];
298         char *expanded = NULL;
299
300         int ret;
301
302         /*
303          *      If we don't have a command, do nothing.
304          */
305         if (!*fmt) return 0;
306
307         /*
308          *      @todo this needs to die (should just be done in xlat expansion)
309          */
310         sqlippool_expand(query, sizeof(query), fmt, data, param, param_len);
311
312         if (radius_axlat(&expanded, request, query, data->sql_inst->sql_escape_func, data->sql_inst) < 0) {
313                 return -1;
314         }
315
316         ret = data->sql_inst->sql_query(data->sql_inst, request, &handle, expanded);
317         if (ret < 0){
318                 talloc_free(expanded);
319                 return -1;
320         }
321         talloc_free(expanded);
322
323         (data->sql_inst->module->sql_finish_query)(handle, data->sql_inst->config);
324         return 0;
325 }
326
327 /*
328  *      Don't repeat yourself
329  */
330 #undef DO
331 #define DO(_x) sqlippool_command(inst->_x, handle, inst, request, NULL, 0)
332
333 /*
334  * Query the database expecting a single result row
335  */
336 static int CC_HINT(nonnull (1, 3, 4, 5)) sqlippool_query1(char *out, int outlen, char const *fmt,
337                                                           rlm_sql_handle_t *handle, rlm_sqlippool_t *data,
338                                                           REQUEST *request, char *param, int param_len)
339 {
340         char query[MAX_QUERY_LEN];
341         char *expanded = NULL;
342
343         int rlen, retval;
344
345         rlm_sql_row_t row;
346
347         /*
348          *      @todo this needs to die (should just be done in xlat expansion)
349          */
350         sqlippool_expand(query, sizeof(query), fmt, data, param, param_len);
351
352         *out = '\0';
353
354         /*
355          *      Do an xlat on the provided string
356          */
357         if (radius_axlat(&expanded, request, query, data->sql_inst->sql_escape_func, data->sql_inst) < 0) {
358                 return 0;
359         }
360         retval = data->sql_inst->sql_select_query(data->sql_inst, request, &handle, expanded);
361         talloc_free(expanded);
362
363         if (retval != 0){
364                 REDEBUG("database query error on '%s'", query);
365                 return 0;
366         }
367
368         if (data->sql_inst->sql_fetch_row(&row, data->sql_inst, request, &handle) < 0) {
369                 REDEBUG("Failed fetching query result");
370                 goto finish;
371         }
372
373         if (!row) {
374                 REDEBUG("SQL query did not return any results");
375                 goto finish;
376         }
377
378         if (!row[0]) {
379                 REDEBUG("The first column of the result was NULL");
380                 goto finish;
381         }
382
383         rlen = strlen(row[0]);
384         if (rlen >= outlen) {
385                 RDEBUG("insufficient string space");
386                 goto finish;
387         }
388
389         strcpy(out, row[0]);
390         retval = rlen;
391 finish:
392         (data->sql_inst->module->sql_finish_select_query)(handle, data->sql_inst->config);
393
394         return retval;
395 }
396
397 /*
398  *      Do any per-module initialization that is separate to each
399  *      configured instance of the module.  e.g. set up connections
400  *      to external databases, read configuration files, set up
401  *      dictionary entries, etc.
402  *
403  *      If configuration information is given in the config section
404  *      that must be referenced in later calls, store a handle to it
405  *      in *instance otherwise put a null pointer there.
406  */
407 static int mod_instantiate(CONF_SECTION *conf, void *instance)
408 {
409         module_instance_t *sql_inst;
410         rlm_sqlippool_t *inst = instance;
411         char const *pool_name = NULL;
412
413         pool_name = cf_section_name2(conf);
414         if (pool_name != NULL) {
415                 inst->pool_name = talloc_typed_strdup(inst, pool_name);
416         } else {
417                 inst->pool_name = talloc_typed_strdup(inst, "ippool");
418         }
419         sql_inst = find_module_instance(cf_section_find("modules"),
420                                        inst->sql_instance_name, true);
421         if (!sql_inst) {
422                 cf_log_err_cs(conf, "failed to find sql instance named %s",
423                            inst->sql_instance_name);
424                 return -1;
425         }
426
427         if (!inst->ipv6) {
428                 inst->framed_ip_address = PW_FRAMED_IP_ADDRESS;
429         } else {
430                 inst->framed_ip_address = PW_FRAMED_IPV6_PREFIX;
431         }
432
433         if (strcmp(sql_inst->entry->name, "rlm_sql") != 0) {
434                 cf_log_err_cs(conf, "Module \"%s\""
435                        " is not an instance of the rlm_sql module",
436                        inst->sql_instance_name);
437                 return -1;
438         }
439
440         inst->sql_inst = (rlm_sql_t *) sql_inst->insthandle;
441         return 0;
442 }
443
444
445 /*
446  *      If we have something to log, then we log it.
447  *      Otherwise we return the retcode as soon as possible
448  */
449 static int do_logging(REQUEST *request, char const *str, int rcode)
450 {
451         char *expanded = NULL;
452
453         if (!str || !*str) return rcode;
454
455         if (radius_axlat(&expanded, request, str, NULL, NULL) < 0) {
456                 return rcode;
457         }
458
459         pairmake_config("Module-Success-Message", expanded, T_OP_SET);
460
461         talloc_free(expanded);
462
463         return rcode;
464 }
465
466
467 /*
468  *      Allocate an IP number from the pool.
469  */
470 static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *request)
471 {
472         rlm_sqlippool_t *inst = (rlm_sqlippool_t *) instance;
473         char allocation[MAX_STRING_LEN];
474         int allocation_len;
475         VALUE_PAIR *vp;
476         rlm_sql_handle_t *handle;
477         time_t now;
478
479         /*
480          *      If there is a Framed-IP-Address attribute in the reply do nothing
481          */
482         if (pairfind(request->reply->vps, inst->framed_ip_address, 0, TAG_ANY) != NULL) {
483                 RDEBUG("Framed-IP-Address already exists");
484
485                 return do_logging(request, inst->log_exists, RLM_MODULE_NOOP);
486         }
487
488         if (pairfind(request->config_items, PW_POOL_NAME, 0, TAG_ANY) == NULL) {
489                 RDEBUG("No Pool-Name defined");
490
491                 return do_logging(request, inst->log_nopool, RLM_MODULE_NOOP);
492         }
493
494         handle = fr_connection_get(inst->sql_inst->pool);
495         if (!handle) {
496                 REDEBUG("cannot get sql connection");
497                 return RLM_MODULE_FAIL;
498         }
499
500         if (inst->sql_inst->sql_set_user(inst->sql_inst, request, NULL) < 0) {
501                 return RLM_MODULE_FAIL;
502         }
503
504         /*
505          *      Limit the number of clears we do.  There are minor
506          *      race conditions for the check, but so what.  The
507          *      actual work is protected by a transaction.  The idea
508          *      here is that if we're allocating 100 IPs a second,
509          *      we're only do 1 CLEAR per second.
510          */
511         now = time(NULL);
512         if (inst->last_clear < now) {
513                 inst->last_clear = now;
514
515                 DO(allocate_begin);
516                 DO(allocate_clear);
517                 DO(allocate_commit);
518         }
519
520         DO(allocate_begin);
521
522         allocation_len = sqlippool_query1(allocation, sizeof(allocation),
523                                           inst->allocate_find, handle,
524                                           inst, request, (char *) NULL, 0);
525
526         /*
527          *      Nothing found...
528          */
529         if (allocation_len == 0) {
530                 DO(allocate_commit);
531
532                 /*
533                  *Should we perform pool-check ?
534                  */
535                 if (inst->pool_check && *inst->pool_check) {
536
537                         /*
538                          *Ok, so the allocate-find query found nothing ...
539                          *Let's check if the pool exists at all
540                          */
541                         allocation_len = sqlippool_query1(allocation, sizeof(allocation),
542                                                           inst->pool_check, handle, inst, request,
543                                                           (char *) NULL, 0);
544
545                         fr_connection_release(inst->sql_inst->pool, handle);
546
547                         if (allocation_len) {
548
549                                 /*
550                                  *      Pool exists after all... So,
551                                  *      the failure to allocate the IP
552                                  *      address was most likely due to
553                                  *      the depletion of the pool. In
554                                  *      that case, we should return
555                                  *      NOTFOUND
556                                  */
557                                 RDEBUG("pool appears to be full");
558                                 return do_logging(request, inst->log_failed, RLM_MODULE_NOTFOUND);
559
560                         }
561
562                         /*
563                          *      Pool doesn't exist in the table. It
564                          *      may be handled by some other instance of
565                          *      sqlippool, so we should just ignore this
566                          *      allocation failure and return NOOP
567                          */
568                         RDEBUG("IP address could not be allocated as no pool exists with that name");
569                         return RLM_MODULE_NOOP;
570
571                 }
572
573                 fr_connection_release(inst->sql_inst->pool, handle);
574
575                 RDEBUG("IP address could not be allocated");
576                 return do_logging(request, inst->log_failed, RLM_MODULE_NOOP);
577         }
578
579         /*
580          *      See if we can create the VP from the returned data.  If not,
581          *      error out.  If so, add it to the list.
582          */
583         vp = paircreate(request->reply, inst->framed_ip_address, 0);
584         if (pairparsevalue(vp, allocation, allocation_len) < 0) {
585                 DO(allocate_commit);
586
587                 RDEBUG("Invalid IP number [%s] returned from instbase query.", allocation);
588                 fr_connection_release(inst->sql_inst->pool, handle);
589                 return do_logging(request, inst->log_failed, RLM_MODULE_NOOP);
590         }
591
592         RDEBUG("Allocated IP %s", allocation);
593         pairadd(&request->reply->vps, vp);
594
595         /*
596          *      UPDATE
597          */
598         sqlippool_command(inst->allocate_update, handle, inst, request,
599                           allocation, allocation_len);
600
601         DO(allocate_commit);
602
603         fr_connection_release(inst->sql_inst->pool, handle);
604
605         return do_logging(request, inst->log_success, RLM_MODULE_OK);
606 }
607
608 static int mod_accounting_start(rlm_sql_handle_t *handle,
609                                       rlm_sqlippool_t *inst, REQUEST *request)
610 {
611         DO(start_begin);
612         DO(start_update);
613         DO(start_commit);
614
615         return RLM_MODULE_OK;
616 }
617
618 static int mod_accounting_alive(rlm_sql_handle_t *handle,
619                                       rlm_sqlippool_t *inst, REQUEST *request)
620 {
621         DO(alive_begin);
622         DO(alive_update);
623         DO(alive_commit);
624         return RLM_MODULE_OK;
625 }
626
627 static int mod_accounting_stop(rlm_sql_handle_t *handle,
628                                       rlm_sqlippool_t *inst, REQUEST *request)
629 {
630         DO(stop_begin);
631         DO(stop_clear);
632         DO(stop_commit);
633
634         return do_logging(request, inst->log_clear, RLM_MODULE_OK);
635 }
636
637 static int mod_accounting_on(rlm_sql_handle_t *handle,
638                                       rlm_sqlippool_t *inst, REQUEST *request)
639 {
640         DO(on_begin);
641         DO(on_clear);
642         DO(on_commit);
643
644         return RLM_MODULE_OK;
645 }
646
647 static int mod_accounting_off(rlm_sql_handle_t *handle,
648                                       rlm_sqlippool_t *inst, REQUEST *request)
649 {
650         DO(off_begin);
651         DO(off_clear);
652         DO(off_commit);
653
654         return RLM_MODULE_OK;
655 }
656
657 /*
658  *      Check for an Accounting-Stop
659  *      If we find one and we have allocated an IP to this nas/port
660  *      combination, then deallocate it.
661  */
662 static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *request)
663 {
664         int rcode = RLM_MODULE_NOOP;
665         VALUE_PAIR *vp;
666         int acct_status_type;
667         rlm_sqlippool_t *inst = (rlm_sqlippool_t *) instance;
668         rlm_sql_handle_t *handle;
669
670         vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY);
671         if (!vp) {
672                 RDEBUG("Could not find account status type in packet");
673                 return RLM_MODULE_NOOP;
674         }
675         acct_status_type = vp->vp_integer;
676
677         switch (acct_status_type) {
678         case PW_STATUS_START:
679         case PW_STATUS_ALIVE:
680         case PW_STATUS_STOP:
681         case PW_STATUS_ACCOUNTING_ON:
682         case PW_STATUS_ACCOUNTING_OFF:
683                 break;          /* continue through to the next section */
684
685         default:
686                 /* We don't care about any other accounting packet */
687                 return RLM_MODULE_NOOP;
688         }
689
690         handle = fr_connection_get(inst->sql_inst->pool);
691         if (!handle) {
692                 RDEBUG("Cannot allocate sql connection");
693                 return RLM_MODULE_FAIL;
694         }
695
696         if (inst->sql_inst->sql_set_user(inst->sql_inst, request, NULL) < 0) {
697                 return RLM_MODULE_FAIL;
698         }
699
700         switch (acct_status_type) {
701         case PW_STATUS_START:
702                 rcode = mod_accounting_start(handle, inst, request);
703                 break;
704
705         case PW_STATUS_ALIVE:
706                 rcode = mod_accounting_alive(handle, inst, request);
707                 break;
708
709         case PW_STATUS_STOP:
710                 rcode = mod_accounting_stop(handle, inst, request);
711                 break;
712
713         case PW_STATUS_ACCOUNTING_ON:
714                 rcode = mod_accounting_on(handle, inst, request);
715                 break;
716
717         case PW_STATUS_ACCOUNTING_OFF:
718                 rcode = mod_accounting_off(handle, inst, request);
719                 break;
720         }
721
722         fr_connection_release(inst->sql_inst->pool, handle);
723
724         return rcode;
725 }
726
727 /*
728  *      The module name should be the only globally exported symbol.
729  *      That is, everything else should be 'static'.
730  *
731  *      If the module needs to temporarily modify it's instantiation
732  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
733  *      The server will then take care of ensuring that the module
734  *      is single-threaded.
735  */
736 extern module_t rlm_sqlippool;
737 module_t rlm_sqlippool = {
738         RLM_MODULE_INIT,
739         "sqlippool",
740         RLM_TYPE_THREAD_SAFE,           /* type */
741         sizeof(rlm_sqlippool_t),
742         module_config,
743         mod_instantiate,                /* instantiation */
744         NULL,                           /* detach */
745         {
746                 NULL,                   /* authentication */
747                 NULL,                   /* authorization */
748                 NULL,                   /* preaccounting */
749                 mod_accounting,         /* accounting */
750                 NULL,                   /* checksimul */
751                 NULL,                   /* pre-proxy */
752                 NULL,                   /* post-proxy */
753                 mod_post_auth           /* post-auth */
754         },
755 };