ff116026eb66c4330d6fea285b5993551e569ce6
[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 != '%' && c != '$' && c != '\\') {
246                         *q++ = *p;
247                         continue;
248                 }
249
250                 if (*++p == '\0')
251                         break;
252
253                 if (c == '\\') {
254                         switch(*p) {
255                         case '\\':
256                                 *q++ = '\\';
257                                 break;
258                         case 't':
259                                 *q++ = '\t';
260                                 break;
261                         case 'n':
262                                 *q++ = '\n';
263                                 break;
264                         default:
265                                 *q++ = c;
266                                 *q++ = *p;
267                                 break;
268                         }
269                 }
270                 else if (c == '%') {
271                         switch(*p) {
272                         case '%':
273                                 *q++ = *p;
274                                 break;
275                         case 'P': /* pool name */
276                                 strlcpy(q, data->pool_name, freespace);
277                                 q += strlen(q);
278                                 break;
279                         case 'I': /* IP address */
280                                 if (param && param_len > 0) {
281                                         if (param_len > freespace) {
282                                                 strlcpy(q, param, freespace);
283                                                 q += strlen(q);
284                                         }
285                                         else {
286                                                 memcpy(q, param, param_len);
287                                                 q += param_len;
288                                         }
289                                 }
290                                 break;
291                         case 'J': /* lease duration */
292                                 sprintf(tmp, "%d", data->lease_duration);
293                                 strlcpy(q, tmp, freespace);
294                                 q += strlen(q);
295                                 break;
296                         default:
297                                 *q++ = '%';
298                                 *q++ = *p;
299                                 break;
300                         }
301                 }
302         }
303         *q = '\0';
304
305 #if 0
306         DEBUG2("sqlippool_expand: \"%s\"", out);
307 #endif
308
309         return strlen(out);
310 }
311
312 /** Perform a single sqlippool query
313  *
314  * Mostly wrapper around sql_query which does some special sqlippool sequence substitutions and expands
315  * the format string.
316  *
317  * @param fmt sql query to expand.
318  * @param handle sql connection handle.
319  * @param data Instance of rlm_sqlippool.
320  * @param request Current request.
321  * @param param ip address string.
322  * @param param_len ip address string len.
323  * @return 0 on success or < 0 on error.
324  */
325 static int sqlippool_command(char const * fmt, rlm_sql_handle_t * handle, rlm_sqlippool_t *data, REQUEST *request,
326                              char *param, int param_len)
327 {
328         char query[MAX_QUERY_LEN];
329         char *expanded = NULL;
330
331         int ret;
332
333         /*
334          *      If we don't have a command, do nothing.
335          */
336         if (!*fmt) return 0;
337
338         /*
339          *      @todo this needs to die (should just be done in xlat expansion)
340          */
341         sqlippool_expand(query, sizeof(query), fmt, data, param, param_len);
342
343         if (radius_axlat(&expanded, request, query, data->sql_inst->sql_escape_func, data->sql_inst) < 0) {
344                 return -1;
345         }
346
347         ret = data->sql_inst->sql_query(&handle, data->sql_inst, expanded);
348         if (ret < 0){
349                 talloc_free(expanded);
350                 return -1;
351         }
352         talloc_free(expanded);
353
354         (data->sql_inst->module->sql_finish_query)(handle, data->sql_inst->config);
355         return 0;
356 }
357
358 /*
359  *      Don't repeat yourself
360  */
361 #undef DO
362 #define DO(_x) sqlippool_command(inst->_x, handle, inst, request, NULL, 0)
363
364 /*
365  * Query the database expecting a single result row
366  */
367 static int sqlippool_query1(char *out, int outlen, char const *fmt,
368                             rlm_sql_handle_t *handle, rlm_sqlippool_t *data,
369                             REQUEST *request, char *param, int param_len)
370 {
371         char query[MAX_QUERY_LEN];
372         char *expanded = NULL;
373
374         int rlen, retval;
375
376         /*
377          *      @todo this needs to die (should just be done in xlat expansion)
378          */
379         sqlippool_expand(query, sizeof(query), fmt, data, param, param_len);
380
381         rad_assert(request != NULL);
382
383         *out = '\0';
384
385         /*
386          *      Do an xlat on the provided string
387          */
388         if (radius_axlat(&expanded, request, query, data->sql_inst->sql_escape_func, data->sql_inst) < 0) {
389                 return 0;
390         }
391         retval = data->sql_inst->sql_select_query(&handle, data->sql_inst, expanded);
392         talloc_free(expanded);
393
394         if (retval != 0){
395                 REDEBUG("database query error on '%s'", query);
396                 return 0;
397         }
398
399         if (data->sql_inst->sql_fetch_row(&handle, data->sql_inst) < 0) {
400                 REDEBUG("Failed fetching query result");
401                 goto finish;
402         }
403
404         if (!handle->row) {
405                 REDEBUG("SQL query did not return any results");
406                 goto finish;
407         }
408
409         if (!handle->row[0]) {
410                 REDEBUG("The first column of the result was NULL");
411                 goto finish;
412         }
413
414         rlen = strlen(handle->row[0]);
415         if (rlen >= outlen) {
416                 RDEBUG("insufficient string space");
417                 goto finish;
418         }
419
420         strcpy(out, handle->row[0]);
421         retval = rlen;
422 finish:
423         (data->sql_inst->module->sql_finish_select_query)(handle, data->sql_inst->config);
424
425         return retval;
426 }
427
428 /*
429  *      Do any per-module initialization that is separate to each
430  *      configured instance of the module.  e.g. set up connections
431  *      to external databases, read configuration files, set up
432  *      dictionary entries, etc.
433  *
434  *      If configuration information is given in the config section
435  *      that must be referenced in later calls, store a handle to it
436  *      in *instance otherwise put a null pointer there.
437  */
438 static int mod_instantiate(CONF_SECTION *conf, void *instance)
439 {
440         module_instance_t *sqlinst;
441         rlm_sqlippool_t *inst = instance;
442         char const *pool_name = NULL;
443
444         pool_name = cf_section_name2(conf);
445         if (pool_name != NULL)
446                 inst->pool_name = talloc_strdup(inst, pool_name);
447         else
448                 inst->pool_name = talloc_strdup(inst, "ippool");
449
450         sqlinst = find_module_instance(cf_section_find("modules"),
451                                        inst->sql_instance_name, 1);
452         if (!sqlinst) {
453                 cf_log_err_cs(conf, "failed to find sql instance named %s",
454                            inst->sql_instance_name);
455                 return -1;
456         }
457
458         if (strcmp(sqlinst->entry->name, "rlm_sql") != 0) {
459                 cf_log_err_cs(conf, "Module \"%s\""
460                        " is not an instance of the rlm_sql module",
461                        inst->sql_instance_name);
462                 return -1;
463         }
464
465         inst->sql_inst = (rlm_sql_t *) sqlinst->insthandle;
466         return 0;
467 }
468
469
470 /*
471  *      If we have something to log, then we log it.
472  *      Otherwise we return the retcode as soon as possible
473  */
474 static int do_logging(REQUEST *request, char *str, int rcode)
475 {
476         char *expanded = NULL;
477
478         if (!str || !*str) return rcode;
479
480         if (radius_axlat(&expanded, request, str, NULL, NULL) < 0) {
481                 return rcode;
482         }
483
484         pairmake_config("Module-Success-Message", expanded, T_OP_SET);
485
486         talloc_free(expanded);
487
488         return rcode;
489 }
490
491
492 /*
493  *      Allocate an IP number from the pool.
494  */
495 static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
496 {
497         rlm_sqlippool_t *inst = (rlm_sqlippool_t *) instance;
498         char allocation[MAX_STRING_LEN];
499         int allocation_len;
500         uint32_t ip_allocation;
501         VALUE_PAIR *vp;
502         rlm_sql_handle_t *handle;
503         fr_ipaddr_t ipaddr;
504         time_t now;
505
506         /*
507          *      If there is a Framed-IP-Address attribute in the reply do nothing
508          */
509         if (pairfind(request->reply->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY) != NULL) {
510                 RDEBUG("Framed-IP-Address already exists");
511
512                 return do_logging(request, inst->log_exists, RLM_MODULE_NOOP);
513         }
514
515         if (pairfind(request->config_items, PW_POOL_NAME, 0, TAG_ANY) == NULL) {
516                 RDEBUG("No Pool-Name defined.");
517
518                 return do_logging(request, inst->log_nopool, RLM_MODULE_NOOP);
519         }
520
521         handle = inst->sql_inst->sql_get_socket(inst->sql_inst);
522         if (!handle) {
523                 REDEBUG("cannot get sql connection");
524                 return RLM_MODULE_FAIL;
525         }
526
527         if (inst->sql_inst->sql_set_user(inst->sql_inst, request, NULL) < 0) {
528                 return RLM_MODULE_FAIL;
529         }
530
531         /*
532          *      Limit the number of clears we do.  There are minor
533          *      race conditions for the check, but so what.  The
534          *      actual work is protected by a transaction.  The idea
535          *      here is that if we're allocating 100 IPs a second,
536          *      we're only do 1 CLEAR per second.
537          */
538         now = time(NULL);
539         if (inst->last_clear < now) {
540                 inst->last_clear = now;
541
542                 DO(allocate_begin);
543                 DO(allocate_clear);
544                 DO(allocate_commit);
545         }
546
547         DO(allocate_begin);
548
549         allocation_len = sqlippool_query1(allocation, sizeof(allocation),
550                                           inst->allocate_find, handle,
551                                           inst, request, (char *) NULL, 0);
552
553         /*
554          *      Nothing found...
555          */
556         if (allocation_len == 0) {
557                 DO(allocate_commit);
558
559                 /*
560                  *Should we perform pool-check ?
561                  */
562                 if (inst->pool_check && *inst->pool_check) {
563
564                         /*
565                          *Ok, so the allocate-find query found nothing ...
566                          *Let's check if the pool exists at all
567                          */
568                         allocation_len = sqlippool_query1(allocation, sizeof(allocation),
569                                                           inst->pool_check, handle, inst, request,
570                                                           (char *) NULL, 0);
571
572                         inst->sql_inst->sql_release_socket(inst->sql_inst, handle);
573
574                         if (allocation_len) {
575
576                                 /*
577                                  *      Pool exists after all... So,
578                                  *      the failure to allocate the IP
579                                  *      address was most likely due to
580                                  *      the depletion of the pool. In
581                                  *      that case, we should return
582                                  *      NOTFOUND
583                                  */
584                                 RDEBUG("pool appears to be full");
585                                 return do_logging(request, inst->log_failed, RLM_MODULE_NOTFOUND);
586
587                         }
588
589                         /*
590                          *      Pool doesn't exist in the table. It
591                          *      may be handled by some other instance of
592                          *      sqlippool, so we should just ignore this
593                          *      allocation failure and return NOOP
594                          */
595                         RDEBUG("IP address could not be allocated as no pool exists with that name.");
596                         return RLM_MODULE_NOOP;
597
598                 }
599
600                 inst->sql_inst->sql_release_socket(inst->sql_inst, handle);
601
602                 RDEBUG("IP address could not be allocated.");
603                 return do_logging(request, inst->log_failed, RLM_MODULE_NOOP);
604         }
605
606         /*
607          *      FIXME: Make it work with the ipv6 addresses
608          */
609         if ((ip_hton(allocation, AF_INET, &ipaddr) < 0) ||
610             ((ip_allocation = ipaddr.ipaddr.ip4addr.s_addr) == INADDR_NONE)) {
611                 DO(allocate_commit);
612
613                 RDEBUG("Invalid IP number [%s] returned from instbase query.", allocation);
614                 inst->sql_inst->sql_release_socket(inst->sql_inst, handle);
615                 return do_logging(request, inst->log_failed, RLM_MODULE_NOOP);
616         }
617
618         /*
619          *      UPDATE
620          */
621         sqlippool_command(inst->allocate_update, handle, inst, request,
622                           allocation, allocation_len);
623
624         RDEBUG("Allocated IP %s [%08x]", allocation, ip_allocation);
625
626         vp = radius_paircreate(request, &request->reply->vps,
627                                PW_FRAMED_IP_ADDRESS, 0);
628         vp->vp_ipaddr = ip_allocation;
629         vp->length = 4;
630
631         DO(allocate_commit);
632
633         inst->sql_inst->sql_release_socket(inst->sql_inst, handle);
634
635         return do_logging(request, inst->log_success, RLM_MODULE_OK);
636 }
637
638 static int mod_accounting_start(rlm_sql_handle_t *handle,
639                                       rlm_sqlippool_t *inst, REQUEST *request)
640 {
641         DO(start_begin);
642         DO(start_update);
643         DO(start_commit);
644
645         return RLM_MODULE_OK;
646 }
647
648 static int mod_accounting_alive(rlm_sql_handle_t *handle,
649                                       rlm_sqlippool_t *inst, REQUEST *request)
650 {
651         DO(alive_begin);
652         DO(alive_update);
653         DO(alive_commit);
654         return RLM_MODULE_OK;
655 }
656
657 static int mod_accounting_stop(rlm_sql_handle_t *handle,
658                                       rlm_sqlippool_t *inst, REQUEST *request)
659 {
660         DO(stop_begin);
661         DO(stop_clear);
662         DO(stop_commit);
663
664         return do_logging(request, inst->log_clear, RLM_MODULE_OK);
665 }
666
667 static int mod_accounting_on(rlm_sql_handle_t *handle,
668                                       rlm_sqlippool_t *inst, REQUEST *request)
669 {
670         DO(on_begin);
671         DO(on_clear);
672         DO(on_commit);
673
674         return RLM_MODULE_OK;
675 }
676
677 static int mod_accounting_off(rlm_sql_handle_t *handle,
678                                       rlm_sqlippool_t *inst, REQUEST *request)
679 {
680         DO(off_begin);
681         DO(off_clear);
682         DO(off_commit);
683
684         return RLM_MODULE_OK;
685 }
686
687 /*
688  *      Check for an Accounting-Stop
689  *      If we find one and we have allocated an IP to this nas/port
690  *      combination, then deallocate it.
691  */
692 static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
693 {
694         int rcode = RLM_MODULE_NOOP;
695         VALUE_PAIR *vp;
696         int acct_status_type;
697         rlm_sqlippool_t *inst = (rlm_sqlippool_t *) instance;
698         rlm_sql_handle_t *handle;
699
700         vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY);
701         if (!vp) {
702                 RDEBUG("Could not find account status type in packet.");
703                 return RLM_MODULE_NOOP;
704         }
705         acct_status_type = vp->vp_integer;
706
707         switch (acct_status_type) {
708         case PW_STATUS_START:
709         case PW_STATUS_ALIVE:
710         case PW_STATUS_STOP:
711         case PW_STATUS_ACCOUNTING_ON:
712         case PW_STATUS_ACCOUNTING_OFF:
713                 break;          /* continue through to the next section */
714
715         default:
716                 /* We don't care about any other accounting packet */
717                 return RLM_MODULE_NOOP;
718         }
719
720         handle = inst->sql_inst->sql_get_socket(inst->sql_inst);
721         if (!handle) {
722                 RDEBUG("Cannot allocate sql connection");
723                 return RLM_MODULE_FAIL;
724         }
725
726         if (inst->sql_inst->sql_set_user(inst->sql_inst, request, NULL) < 0) {
727                 return RLM_MODULE_FAIL;
728         }
729
730         switch (acct_status_type) {
731         case PW_STATUS_START:
732                 rcode = mod_accounting_start(handle, inst, request);
733                 break;
734
735         case PW_STATUS_ALIVE:
736                 rcode = mod_accounting_alive(handle, inst, request);
737                 break;
738
739         case PW_STATUS_STOP:
740                 rcode = mod_accounting_stop(handle, inst, request);
741                 break;
742
743         case PW_STATUS_ACCOUNTING_ON:
744                 rcode = mod_accounting_on(handle, inst, request);
745                 break;
746
747         case PW_STATUS_ACCOUNTING_OFF:
748                 rcode = mod_accounting_off(handle, inst, request);
749                 break;
750         }
751
752         inst->sql_inst->sql_release_socket(inst->sql_inst, handle);
753
754         return rcode;
755 }
756
757 /*
758  *      The module name should be the only globally exported symbol.
759  *      That is, everything else should be 'static'.
760  *
761  *      If the module needs to temporarily modify it's instantiation
762  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
763  *      The server will then take care of ensuring that the module
764  *      is single-threaded.
765  */
766 module_t rlm_sqlippool = {
767         RLM_MODULE_INIT,
768         "sqlippool",
769         RLM_TYPE_THREAD_SAFE,           /* type */
770         sizeof(rlm_sqlippool_t),
771         module_config,
772         mod_instantiate,                /* instantiation */
773         NULL,                           /* detach */
774         {
775                 NULL,                   /* authentication */
776                 NULL,                   /* authorization */
777                 NULL,                   /* preaccounting */
778                 mod_accounting, /* accounting */
779                 NULL,                   /* checksimul */
780                 NULL,                   /* pre-proxy */
781                 NULL,                   /* post-proxy */
782                 mod_post_auth   /* post-auth */
783         },
784 };