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