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