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