Removed all free(inst->foo) where "foo" comes from parsing
[freeradius.git] / src / modules / rlm_sqlippool / rlm_sqlippool.c
1 /*
2  *  rlm_sqlippool.c     rlm_sqlippool - FreeRADIUS SQL IP Pool Module
3  *
4  * Version:  $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * Copyright 2002  Globe.Net Communications Limited
21  * Copyright 2006  The FreeRADIUS server project
22  * Copyright 2006  Suntel Communications
23  */
24
25 #include <freeradius-devel/ident.h>
26 RCSID("$Id$")
27
28 #include <freeradius-devel/autoconf.h>
29 #include <freeradius-devel/libradius.h>
30
31 #include <stdlib.h>
32 #include <ctype.h>
33
34 #include <freeradius-devel/radiusd.h>
35 #include <freeradius-devel/modules.h>
36 #include <freeradius-devel/modpriv.h>
37
38 #include <rlm_sql.h>
39
40 /*
41  *      Define a structure for our module configuration.
42  */
43 typedef struct rlm_sqlippool_t {
44         char *sql_instance_name;
45
46         int lease_duration;
47
48         SQL_INST *sql_inst;
49
50         char *pool_name;
51
52         /* We ended up removing the init 
53            queries so that its up to user
54            to create the db structure and put the required
55            information in there                 
56         */
57                                 /* Allocation sequence */
58         char *allocate_begin;   /* SQL query to begin */
59         char *allocate_clear;   /* SQL query to clear an IP */
60         char *allocate_find;    /* SQL query to find an unused IP */
61         char *allocate_update;  /* SQL query to mark an IP as used */
62         char *allocate_commit;  /* SQL query to commit */
63         char *allocate_rollback; /* SQL query to rollback */
64
65         char *pool_check;       /* Query to check for the existence of the pool */
66
67                                 /* Start sequence */
68         char *start_begin;      /* SQL query to begin */
69         char *start_update;     /* SQL query to update an IP entry */
70         char *start_commit;     /* SQL query to commit */
71         char *start_rollback;   /* SQL query to rollback */
72
73                                 /* Alive sequence */
74         char *alive_begin;      /* SQL query to begin */
75         char *alive_update;     /* SQL query to update an IP entry */
76         char *alive_commit;     /* SQL query to commit */
77         char *alive_rollback;   /* SQL query to rollback */
78
79                                 /* Stop sequence */
80         char *stop_begin;       /* SQL query to begin */
81         char *stop_clear;       /* SQL query to clear an IP */
82         char *stop_commit;      /* SQL query to commit */
83         char *stop_rollback;    /* SQL query to rollback */
84
85                                 /* On sequence */
86         char *on_begin;         /* SQL query to begin */
87         char *on_clear;         /* SQL query to clear an entire NAS */
88         char *on_commit;        /* SQL query to commit */
89         char *on_rollback;      /* SQL query to rollback */
90
91                                 /* Off sequence */
92         char *off_begin;        /* SQL query to begin */
93         char *off_clear;        /* SQL query to clear an entire NAS */
94         char *off_commit;       /* SQL query to commit */
95         char *off_rollback;     /* SQL query to rollback */
96         
97                                 /* Logging Section */
98         char *log_exists;       /* There was an ip address already assigned */
99         char *log_success;      /* We successfully allocated ip address from pool */
100         char *log_clear;        /* We successfully deallocated ip address from pool */
101         char *log_failed;       /* Failed to allocate ip from the pool */
102         char *log_nopool;       /* There was no Framed-IP-Address but also no Pool-Name */
103
104                                 /* Reserved to handle 255.255.255.254 Requests */
105         char *defaultpool;      /* Default Pool-Name if there is non in the check items */
106
107 } rlm_sqlippool_t;
108
109 /*
110  *      A mapping of configuration file names to internal variables.
111  *
112  *      Note that the string is dynamically allocated, so it MUST
113  *      be freed.  When the configuration file parse re-reads the string,
114  *      it free's the old one, and strdup's the new one, placing the pointer
115  *      to the strdup'd string into 'config.string'.  This gets around
116  *      buffer over-flows.
117  */
118 static CONF_PARSER module_config[] = {
119   {"sql-instance-name",PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,sql_instance_name), NULL, "sql"},
120
121   { "lease-duration", PW_TYPE_INTEGER, offsetof(rlm_sqlippool_t,lease_duration), NULL, "86400"},
122
123   { "pool-name"     , PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t, pool_name), NULL, ""},
124
125   { "allocate-begin", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,allocate_begin), NULL, "START TRANSACTION" },
126   { "allocate-clear", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,allocate_clear), NULL, "" },
127   { "allocate-find", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,allocate_find), NULL, "" },
128   { "allocate-update", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,allocate_update), NULL, "" },
129   { "allocate-commit", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,allocate_commit), NULL, "COMMIT" },
130   { "allocate-rollback", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,allocate_rollback), NULL, "ROLLBACK" },
131
132   { "pool-check", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,pool_check), NULL, "" },
133
134   { "start-begin", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,start_begin), NULL, "START TRANSACTION" },
135   { "start-update", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,start_update), NULL, "" },
136   { "start-commit", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,start_commit), NULL, "COMMIT" },
137   { "start-rollback", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,start_rollback), NULL, "ROLLBACK" },
138
139   { "alive-begin", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,alive_begin), NULL, "START TRANSACTION" },
140   { "alive-update", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,alive_update), NULL, "" },
141   { "alive-commit", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,alive_commit), NULL, "COMMIT" },
142   { "alive-rollback", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,alive_rollback), NULL, "ROLLBACK" },
143
144   { "stop-begin", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,stop_begin), NULL, "START TRANSACTION" },
145   { "stop-clear", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,stop_clear), NULL, "" },
146   { "stop-commit", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,stop_commit), NULL, "COMMIT" },
147   { "stop-rollback", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,stop_rollback), NULL, "ROLLBACK" },
148
149   { "on-begin", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,on_begin), NULL, "START TRANSACTION" },
150   { "on-clear", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,on_clear), NULL, "" },
151   { "on-commit", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,on_commit), NULL, "COMMIT" },
152   { "on-rollback", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,on_rollback), NULL, "ROLLBACK" },
153
154   { "off-begin", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,off_begin), NULL, "START TRANSACTION" },
155   { "off-clear", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,off_clear), NULL, "" },
156   { "off-commit", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,off_commit), NULL, "COMMIT" },
157   { "off-rollback", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,off_rollback), NULL, "ROLLBACK" },
158
159   { "sqlippool_log_exists", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t, log_exists), NULL, "" },
160   { "sqlippool_log_success", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t, log_success), NULL, "" },
161   { "sqlippool_log_clear", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t, log_clear), NULL, "" },
162   { "sqlippool_log_failed", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t, log_failed), NULL, "" },
163   { "sqlippool_log_nopool", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t, log_nopool), NULL, "" },
164
165   { "defaultpool", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t, defaultpool), NULL, "main_pool" },
166
167   { NULL, -1, 0, NULL, NULL }
168 };
169
170 /*
171  *      Replace %<whatever> in a string.
172  *
173  *      %P      pool_name
174  *      %I      param
175  *      %J      lease_duration
176  *
177  */
178 static int sqlippool_expand(char * out, int outlen, const char * fmt, void * instance, char * param, int param_len)
179 {
180         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
181         char *q;
182         const char *p;
183         char tmp[40]; /* For temporary storing of integers */
184         int openbraces;
185
186         openbraces = 0;
187         q = out;
188         for (p = fmt; *p ; p++) {
189                 int freespace;
190                 int c;
191
192                 /* Calculate freespace in output */
193                 freespace = outlen - (q - out);
194                 if (freespace <= 1)
195                         break;
196
197                 c = *p;
198                 if (c != '%' && c != '$' && c != '\\') {
199                         /*
200                          * We check if we're inside an open brace.  If we are
201                          * then we assume this brace is NOT literal, but is
202                          * a closing brace and apply it 
203                          */
204                         if((c == '}') && openbraces) {
205                                 openbraces--;
206                                 continue;
207                         }
208                         *q++ = *p;
209                         continue;
210                 }
211
212                 if (*++p == '\0')
213                         break;
214
215                 if (c == '\\') {
216                         switch(*p) {
217                         case '\\':
218                                 *q++ = '\\';
219                                 break;
220                         case 't':
221                                 *q++ = '\t';
222                                 break;
223                         case 'n':
224                                 *q++ = '\n';
225                                 break;
226                         default:
227                                 *q++ = c;
228                                 *q++ = *p;
229                                 break;
230                         }
231                 }
232                 else if (c == '%') {
233                         switch(*p) {
234                         case '%':
235                                 *q++ = *p;
236                                 break;
237                         case 'P': /* pool name */
238                                 strlcpy(q, data->pool_name, freespace); 
239                                 q += strlen(q);
240                                 break;
241                         case 'I': /* IP address */
242                                 if (param && param_len > 0) {
243                                         if (param_len > freespace) {
244                                                 strlcpy(q, param, freespace);
245                                                 q += strlen(q);
246                                         }
247                                         else {
248                                                 memcpy(q, param, param_len);
249                                                 q += param_len;
250                                         }
251                                 }
252                                 break;
253                         case 'J': /* lease duration */
254                                 sprintf(tmp, "%d", data->lease_duration);
255                                 strlcpy(q, tmp, freespace); 
256                                 q += strlen(q);
257                                 break;
258                         default:
259                                 *q++ = '%';
260                                 *q++ = *p;
261                                 break;
262                         }
263                 }
264         }
265         *q = '\0';
266
267 #if 0
268         DEBUG2("sqlippool_expand: '%s'", out);
269 #endif
270
271         return strlen(out);
272 }
273
274 /*
275  * Query the database executing a command with no result rows
276  */
277 static int sqlippool_command(const char * fmt, SQLSOCK * sqlsocket, void * instance, REQUEST * request, char * param, int param_len)
278 {
279         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
280         char expansion[MAX_STRING_LEN * 4];
281         char query[MAX_STRING_LEN * 4];
282
283         sqlippool_expand(expansion, sizeof(expansion), fmt, instance, param, param_len);
284
285         /*
286          * Do an xlat on the provided string
287          */
288         if (request) {
289                 if (!radius_xlat(query, sizeof(query), expansion, request, NULL)) {
290                         radlog(L_ERR, "sqlippool_command: xlat failed on: '%s'", query);
291                         return 0;
292                 }
293         }
294         else {
295                 strcpy(query, expansion);
296         }
297
298 #if 0
299         DEBUG2("sqlippool_command: '%s'", query);
300 #endif
301         if (rlm_sql_query(sqlsocket, data->sql_inst, query)){
302                 radlog(L_ERR, "sqlippool_command: database query error in: '%s'", query);
303                 return 0;
304         }
305
306         (data->sql_inst->module->sql_finish_query)(sqlsocket, data->sql_inst->config);
307         return 0;
308 }
309
310 /*
311  * Query the database expecting a single result row
312  */
313 static int sqlippool_query1(char * out, int outlen, const char * fmt, SQLSOCK * sqlsocket, void * instance, REQUEST * request, char * param, int param_len)
314 {
315         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
316         char expansion[MAX_STRING_LEN * 4];
317         char query[MAX_STRING_LEN * 4];
318         SQL_ROW row;
319         int rlen, retval = 0;
320
321         sqlippool_expand(expansion, sizeof(expansion), fmt, instance, param, param_len);
322
323         /*
324          * Do an xlat on the provided string
325          */
326         if (request) {
327                 if (!radius_xlat(query, sizeof(query), expansion, request, NULL)) {
328                         radlog(L_ERR, "sqlippool_command: xlat failed.");
329                         out[0] = '\0';
330                         return 0;
331                 }
332         }
333         else {
334                 strcpy(query, expansion);
335         }
336
337 #if 0
338         DEBUG2("sqlippool_query1: '%s'", query);
339 #endif
340         if (rlm_sql_select_query(sqlsocket, data->sql_inst, query)){
341                 radlog(L_ERR, "sqlippool_query1: database query error");
342                 out[0] = '\0';
343                 return 0;
344         }
345
346         out[0] = '\0';
347
348         if (!rlm_sql_fetch_row(sqlsocket, data->sql_inst)) {
349                 if (sqlsocket->row) {
350                         if (sqlsocket->row[0]) {
351                                 if ((rlen = strlen(sqlsocket->row[0])) < outlen) {
352                                         strcpy(out, sqlsocket->row[0]);
353                                         retval = rlen;
354                                 } else DEBUG("sqlippool_query1: insufficient string space");
355                         } else DEBUG("sqlippool_query1: row[0] returned NULL");
356                 } else DEBUG("sqlippool_query1: SQL query did not return any results");
357         } else DEBUG("sqlippool_query1: SQL query did not succeed");
358
359         (data->sql_inst->module->sql_finish_select_query)(sqlsocket, data->sql_inst->config);
360         return retval;
361 }
362
363 static int sqlippool_initialize_sql(void * instance)
364 {
365
366         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
367
368         SQLSOCK * sqlsocket;
369
370         sqlsocket = sql_get_socket(data->sql_inst);
371         if (sqlsocket == NULL) {
372                 DEBUG("rlm_sqlippool: cannot allocate sql connection for initialization sequence");
373                 return 0;
374         }
375
376         return 1;
377 }
378
379 /*
380  *      Do any per-module initialization that is separate to each
381  *      configured instance of the module.  e.g. set up connections
382  *      to external databases, read configuration files, set up
383  *      dictionary entries, etc.
384  *
385  *      If configuration information is given in the config section
386  *      that must be referenced in later calls, store a handle to it
387  *      in *instance otherwise put a null pointer there.
388  */
389 static int sqlippool_instantiate(CONF_SECTION * conf, void ** instance)
390 {
391         rlm_sqlippool_t * data;
392         char * pool_name = NULL;
393
394         /*
395          *      Set up a storage area for instance data
396          */
397         data = rad_malloc(sizeof(*data));
398         memset(data, 0, sizeof(*data));
399
400         /*
401          *      If the configuration parameters can't be parsed, then
402          *      fail.
403          */
404         if (cf_section_parse(conf, data, module_config) < 0) {
405                 free(data);
406                 return -1;
407         }
408
409         if (data->sql_instance_name == NULL || strlen(data->sql_instance_name) == 0) {
410                 radlog(L_ERR, "rlm_sqlippool: the 'sql-instance-name' variable must be set.");
411                 free(data);
412                 exit(0);
413         }
414
415         /*
416          *      Check that all the queries are in place
417          */
418
419         if (data->allocate_clear == NULL || strlen(data->allocate_clear) == 0) {
420                 radlog(L_ERR, "rlm_sqlippool: the 'allocate-clear' statement must be set.");
421                 free(data);
422                 exit(0);
423         }
424
425         if (data->allocate_find == NULL || strlen(data->allocate_find) == 0) {
426                 radlog(L_ERR, "rlm_sqlippool: the 'allocate_find' statement must be set.");
427                 free(data);
428                 exit(0);
429         }
430
431         if (data->allocate_update == NULL || strlen(data->allocate_update) == 0) {
432                 radlog(L_ERR, "rlm_sqlippool: the 'allocate_update' statement must be set.");
433                 free(data);
434                 exit(0);
435         }
436
437         if (data->start_update == NULL || strlen(data->start_update) == 0) {
438                 radlog(L_ERR, "rlm_sqlippool: the 'start-update' statement must be set.");
439                 free(data);
440                 exit(0);
441         }
442
443         if (data->alive_update == NULL || strlen(data->alive_update) == 0) {
444                 radlog(L_ERR, "rlm_sqlippool: the 'alive-update' statement must be set.");
445                 free(data);
446                 exit(0);
447         }
448
449         if (data->stop_clear == NULL || strlen(data->stop_clear) == 0) {
450                 radlog(L_ERR, "rlm_sqlippool: the 'stop-clear' statement must be set.");
451                 free(data);
452                 exit(0);
453         }
454
455         if (data->on_clear == NULL || strlen(data->on_clear) == 0) {
456                 radlog(L_ERR, "rlm_sqlippool: the 'on-clear' statement must be set.");
457                 free(data);
458                 exit(0);
459         }
460
461         if (data->off_clear == NULL || strlen(data->off_clear) == 0) {
462                 radlog(L_ERR, "rlm_sqlippool: the 'off-clear' statement must be set.");
463                 free(data);
464                 exit(0);
465         }
466
467         pool_name = cf_section_name2(conf);
468         if (pool_name != NULL)
469                 data->pool_name = strdup(pool_name);
470         else
471                 data->pool_name = strdup("ippool");
472
473         if ( !(data->sql_inst = (SQL_INST *) (find_module_instance(cf_section_find("modules"), data->sql_instance_name))->insthandle) )
474         {
475                 radlog(L_ERR, "sqlippool_instantiate: failed to find sql instance named %s", data->sql_instance_name);
476                 free(data);
477                 exit(0);
478         }
479
480         sqlippool_initialize_sql(data);
481         *instance = data;
482         return 0;
483 }
484
485
486 /*
487  * if we have something to log, then we log it
488  * otherwise we return the retcode as soon as possible 
489  */
490 static int do_logging(char *str, int retcode)
491 {
492         if (strlen(str))
493                 radlog(L_INFO,"%s", str);       
494         return retcode;
495 }
496
497
498 /*
499  *      Allocate an IP number from the pool.
500  */
501 static int sqlippool_postauth(void *instance, REQUEST * request)
502 {
503         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
504         char allocation[MAX_STRING_LEN];
505         int allocation_len;
506         uint32_t ip_allocation;
507         VALUE_PAIR * vp;
508         SQLSOCK * sqlsocket;
509         lrad_ipaddr_t ipaddr;
510
511         VALUE_PAIR *callingsid;
512         VALUE_PAIR *pair;
513
514         int do_callingsid = 0;
515         int do_calledsid = 0;
516
517         char    logstr[MAX_STRING_LEN];
518
519         /*
520          * If there is a Framed-IP-Address attribute in the reply do nothing
521          */
522         if (pairfind(request->reply->vps, PW_FRAMED_IP_ADDRESS) != NULL) {
523                 /* We already have a Framed-IP-Address */
524                 radius_xlat(logstr, sizeof(logstr), data->log_exists, request, NULL);
525                 DEBUG("rlm_sqlippool: Framed-IP-Address already exists");
526
527                 return do_logging(logstr, RLM_MODULE_NOOP);
528         }
529
530         if (pairfind(request->config_items, PW_POOL_NAME) == NULL) {
531                 DEBUG("rlm_sqlippool: We Dont have Pool-Name in check items.. Lets do nothing..");
532                 radius_xlat(logstr, sizeof(logstr), data->log_nopool, request, NULL);
533
534                 return do_logging(logstr, RLM_MODULE_NOOP);
535         }
536         
537         if (pairfind(request->packet->vps, PW_NAS_IP_ADDRESS) == NULL) {
538                 DEBUG("rlm_sqlippool: unknown NAS-IP-Address");
539                 return RLM_MODULE_NOOP;
540         }
541
542         if (pairfind(request->packet->vps, PW_NAS_PORT) == NULL) {
543                 DEBUG("rlm_sqlippool: unknown NAS-Port");
544                 return RLM_MODULE_NOOP;
545         }
546
547         sqlsocket = sql_get_socket(data->sql_inst);
548         if (sqlsocket == NULL) {
549                 DEBUG("rlm_sqlippool: cannot allocate sql connection");
550                 return RLM_MODULE_FAIL;
551         }
552
553         /*
554          * BEGIN
555          */
556         sqlippool_command(data->allocate_begin, sqlsocket, instance, request,
557                           (char *) NULL, 0);
558
559         /*
560          * CLEAR
561          */
562         sqlippool_command(data->allocate_clear, sqlsocket, instance, request,
563                           (char *) NULL, 0);
564
565         /*
566          * FIND
567          */
568         allocation_len = sqlippool_query1(allocation, sizeof(allocation),
569                                           data->allocate_find, sqlsocket, instance, request,
570                                           (char *) NULL, 0);
571
572         if (allocation_len == 0)
573         {       
574                 /*
575                  * COMMIT
576                  */
577                 sqlippool_command(data->allocate_commit, sqlsocket, instance, request,
578                                   (char *) NULL, 0);
579
580                 /*
581                  * Should we perform pool-check ?
582                  */ 
583                 if (data->pool_check && *data->pool_check) {
584
585                         /*
586                          * Ok, so the allocate-find query found nothing ...
587                          * Let's check if the pool exists at all
588                          */
589                         allocation_len = sqlippool_query1(allocation, sizeof(allocation),
590                                                  data->pool_check, sqlsocket, instance, request,
591                                                 (char *) NULL, 0);
592
593                         sql_release_socket(data->sql_inst, sqlsocket);
594
595                         if (allocation_len) {
596
597                                 /*
598                                  * Pool exists after all... So, the failure to allocate
599                                  * the IP address was most likely due to the depletion 
600                                  * of the pool. In that case, we should return NOTFOUND
601                                  */
602                                 DEBUG("rlm_sqlippool: IP address could not be allocated.");
603                                 radius_xlat(logstr, sizeof(logstr), data->log_failed, request, NULL);
604                                 return do_logging(logstr, RLM_MODULE_NOTFOUND);
605
606                         }
607                         /*
608                          * Pool doesn't exist in the table. It may be handled by some 
609                          * other instance of sqlippool, so we should just ignore
610                          * this allocation failure and return NOOP
611                          */
612                         DEBUG("rlm_sqlippool: IP address could not be allocated as not pool exists with that name.");
613                         return RLM_MODULE_NOOP;
614                 
615                 }
616                 
617                 sql_release_socket(data->sql_inst, sqlsocket);
618
619                 DEBUG("rlm_sqlippool: IP address could not be allocated.");
620                 radius_xlat(logstr, sizeof(logstr), data->log_failed, request, NULL);
621
622                 return do_logging(logstr, RLM_MODULE_NOOP);
623         }
624
625         
626         /*
627          *  FIXME: Make it work with the ipv6 addresses
628          */
629         if ((ip_hton(allocation, AF_INET, &ipaddr) < 0) ||
630             ((ip_allocation = ipaddr.ipaddr.ip4addr.s_addr) == INADDR_NONE))
631         {
632                 /*
633                  * COMMIT
634                  */
635                 sqlippool_command(data->allocate_commit, sqlsocket, instance, request,
636                                   (char *) NULL, 0);
637
638                 DEBUG("rlm_sqlippool: Invalid IP number [%s] returned from database query.", allocation);
639                 sql_release_socket(data->sql_inst, sqlsocket);
640                 radius_xlat(logstr, sizeof(logstr), data->log_failed, request, NULL);   
641                 
642                 return do_logging(logstr, RLM_MODULE_NOOP);
643         }
644
645         /*
646          * UPDATE
647          */
648         sqlippool_command(data->allocate_update, sqlsocket, instance, request,
649                           allocation, allocation_len);
650
651         DEBUG("rlm_sqlippool: Allocated IP %s [%08x]", allocation, ip_allocation);
652
653         if ((vp = paircreate(PW_FRAMED_IP_ADDRESS, PW_TYPE_IPADDR)) == NULL) {
654                 radlog(L_ERR|L_CONS, "no memory");
655                 sql_release_socket(data->sql_inst, sqlsocket);
656                 return RLM_MODULE_NOOP;
657         }
658         vp->lvalue = ip_allocation;
659         pairadd(&request->reply->vps, vp);
660
661         /*
662          * COMMIT
663          */
664         sqlippool_command(data->allocate_commit, sqlsocket, instance, request,
665                           (char *) NULL, 0);
666
667         sql_release_socket(data->sql_inst, sqlsocket);
668         radius_xlat(logstr, sizeof(logstr), data->log_success, request, NULL);
669
670         return do_logging(logstr, RLM_MODULE_OK);
671 }
672
673 static int sqlippool_accounting_start(void * instance, REQUEST * request)
674 {
675         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
676         SQLSOCK * sqlsocket;
677
678         if (pairfind(request->packet->vps, PW_NAS_PORT) == NULL) {
679                 DEBUG("rlm_ippool: Could not find port number in packet.");
680                 return RLM_MODULE_NOOP;
681         }
682
683         if (pairfind(request->packet->vps, PW_NAS_IP_ADDRESS) == NULL) {
684                 DEBUG("rlm_ippool: Could not find nas information in packet.");
685                 return RLM_MODULE_NOOP;
686         }
687
688         sqlsocket = sql_get_socket(data->sql_inst);
689         if (sqlsocket == NULL) {
690                 DEBUG("rlm_sqlippool: cannot allocate sql connection");
691                 return RLM_MODULE_NOOP;
692         }
693
694         /*
695          * BEGIN
696          */
697         sqlippool_command(data->start_begin, sqlsocket, instance, request,
698                           (char *) NULL, 0);
699
700         /*
701          * UPDATE
702          */
703         sqlippool_command(data->start_update, sqlsocket, instance, request,
704                           (char *) NULL, 0);
705
706         /*
707          * COMMIT
708          */
709         sqlippool_command(data->start_commit, sqlsocket, instance, request,
710                           (char *) NULL, 0);
711
712         sql_release_socket(data->sql_inst, sqlsocket);
713
714         return RLM_MODULE_OK;
715 }
716
717 static int sqlippool_accounting_alive(void * instance, REQUEST * request)
718 {
719         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
720         SQLSOCK * sqlsocket;
721
722         if (pairfind(request->packet->vps, PW_NAS_PORT) == NULL) {
723                 DEBUG("rlm_ippool: Could not find port number in packet.");
724                 return RLM_MODULE_NOOP;
725         }
726
727         if (pairfind(request->packet->vps, PW_NAS_IP_ADDRESS) == NULL) {
728                 DEBUG("rlm_ippool: Could not find nas information in packet.");
729                 return RLM_MODULE_NOOP;
730         }
731
732         sqlsocket = sql_get_socket(data->sql_inst);
733         if (sqlsocket == NULL) {
734                 DEBUG("rlm_sqlippool: cannot allocate sql connection");
735                 return RLM_MODULE_NOOP;
736         }
737
738         /*
739          * BEGIN
740          */
741         sqlippool_command(data->alive_begin, sqlsocket, instance, request,
742                           (char *) NULL, 0);
743
744         /*
745          * UPDATE
746          */
747         sqlippool_command(data->alive_update, sqlsocket, instance, request,
748                           (char *) NULL, 0);
749
750         /*
751          * COMMIT
752          */
753         sqlippool_command(data->alive_commit, sqlsocket, instance, request,
754                           (char *) NULL, 0);
755
756         sql_release_socket(data->sql_inst, sqlsocket);
757
758         return RLM_MODULE_OK;
759 }
760
761 static int sqlippool_accounting_stop(void * instance, REQUEST * request)
762 {
763         char    logstr[MAX_STRING_LEN];
764
765         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
766         SQLSOCK * sqlsocket;
767
768         if (pairfind(request->packet->vps, PW_NAS_PORT) == NULL) {
769                 DEBUG("rlm_ippool: Could not find port number in packet.");
770                 return RLM_MODULE_NOOP;
771         }
772
773         if (pairfind(request->packet->vps, PW_NAS_IP_ADDRESS) == NULL) {
774                 DEBUG("rlm_ippool: Could not find nas information in packet.");
775                 return RLM_MODULE_NOOP;
776         }
777
778         sqlsocket = sql_get_socket(data->sql_inst);
779         if (sqlsocket == NULL) {
780                 DEBUG("rlm_sqlippool: cannot allocate sql connection");
781                 return RLM_MODULE_NOOP;
782         }
783
784         /*
785          * BEGIN
786          */
787         sqlippool_command(data->stop_begin, sqlsocket, instance, request,
788                           (char *) NULL, 0);
789
790         /*
791          * CLEAR
792          */
793         sqlippool_command(data->stop_clear, sqlsocket, instance, request,
794                           (char *) NULL, 0);
795
796         /*
797          * COMMIT
798          */
799         sqlippool_command(data->stop_commit, sqlsocket, instance, request,
800                           (char *) NULL, 0);
801
802         sql_release_socket(data->sql_inst, sqlsocket);
803         radius_xlat(logstr, sizeof(logstr), data->log_clear, request, NULL);
804
805         return do_logging(logstr, RLM_MODULE_OK);
806 }
807
808 static int sqlippool_accounting_on(void * instance, REQUEST * request)
809 {
810         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
811         SQLSOCK * sqlsocket;
812
813         if (pairfind(request->packet->vps, PW_NAS_IP_ADDRESS) == NULL) {
814                 DEBUG("rlm_ippool: Could not find nas information in packet.");
815                 return RLM_MODULE_NOOP;
816         }
817
818         sqlsocket = sql_get_socket(data->sql_inst);
819         if (sqlsocket == NULL) {
820                 DEBUG("rlm_sqlippool: cannot allocate sql connection");
821                 return RLM_MODULE_NOOP;
822         }
823
824         /*
825          * BEGIN
826          */
827         sqlippool_command(data->on_begin, sqlsocket, instance, request,
828                           (char *) NULL, 0);
829
830         /*
831          * CLEAR
832          */
833         sqlippool_command(data->on_clear, sqlsocket, instance, request,
834                           (char *) NULL, 0);
835
836         /*
837          * COMMIT
838          */
839         sqlippool_command(data->on_commit, sqlsocket, instance, request,
840                           (char *) NULL, 0);
841
842         sql_release_socket(data->sql_inst, sqlsocket);
843
844         return RLM_MODULE_OK;
845 }
846
847 static int sqlippool_accounting_off(void * instance, REQUEST * request)
848 {
849         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
850         SQLSOCK * sqlsocket;
851
852         if (pairfind(request->packet->vps, PW_NAS_IP_ADDRESS) == NULL) {
853                 DEBUG("rlm_ippool: Could not find nas information in packet.");
854                 return RLM_MODULE_NOOP;
855         }
856
857         sqlsocket = sql_get_socket(data->sql_inst);
858         if (sqlsocket == NULL) {
859                 DEBUG("rlm_sqlippool: cannot allocate sql connection");
860                 return RLM_MODULE_NOOP;
861         }
862
863         /*
864          * BEGIN
865          */
866         sqlippool_command(data->off_begin, sqlsocket, instance, request,
867                           (char *) NULL, 0);
868
869         /*
870          * CLEAR
871          */
872         sqlippool_command(data->off_clear, sqlsocket, instance, request,
873                           (char *) NULL, 0);
874
875         /*
876          * COMMIT
877          */
878         sqlippool_command(data->off_commit, sqlsocket, instance, request,
879                           (char *) NULL, 0);
880
881         sql_release_socket(data->sql_inst, sqlsocket);
882
883         return RLM_MODULE_OK;
884 }
885
886 /*
887  *      Check for an Accounting-Stop
888  *      If we find one and we have allocated an IP to this nas/port combination, deallocate it. 
889  */
890 static int sqlippool_accounting(void * instance, REQUEST * request)
891 {
892         VALUE_PAIR * vp;
893         int acct_status_type;
894
895         vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE);
896         if (vp == NULL) {
897                 DEBUG("rlm_sqlippool: Could not find account status type in packet.");
898                 return RLM_MODULE_NOOP;
899         }
900         acct_status_type = vp->lvalue;
901
902         switch (acct_status_type) {
903         case PW_STATUS_START:
904                 return sqlippool_accounting_start(instance, request);
905
906         case PW_STATUS_ALIVE:
907                 return sqlippool_accounting_alive(instance, request);
908
909         case PW_STATUS_STOP:
910                 return sqlippool_accounting_stop(instance, request);
911
912         case PW_STATUS_ACCOUNTING_ON:
913                 return sqlippool_accounting_on(instance, request);
914
915         case PW_STATUS_ACCOUNTING_OFF:
916                 return sqlippool_accounting_off(instance, request);
917
918         default:
919                 /* We don't care about any other accounting packet */
920                 return RLM_MODULE_NOOP;
921         }
922 }
923
924 static int sqlippool_detach(void *instance)
925 {
926         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
927
928         return 0;
929 }
930
931 /*
932  *      The module name should be the only globally exported symbol.
933  *      That is, everything else should be 'static'.
934  *
935  *      If the module needs to temporarily modify it's instantiation
936  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
937  *      The server will then take care of ensuring that the module
938  *      is single-threaded.
939  */
940 module_t rlm_sqlippool = {
941         RLM_MODULE_INIT,
942         "SQL IP Pool",  
943         RLM_TYPE_THREAD_SAFE,           /* type */
944         sqlippool_instantiate,          /* instantiation */
945         sqlippool_detach,               /* detach */
946         {
947                 NULL,                   /* authentication */
948                 NULL,                   /* authorization */
949                 NULL,                   /* preaccounting */
950                 sqlippool_accounting,   /* accounting */
951                 NULL,                   /* checksimul */
952                 NULL,                   /* pre-proxy */
953                 NULL,                   /* post-proxy */
954                 sqlippool_postauth      /* post-auth */
955         },
956 };