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