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