Made it 2.0.0, and removed the changes that are in 1.1.x, as
[freeradius.git] / src / modules / rlm_sqlippool / rlm_sqlippool.c
1 /*
2  * rlm_sqlippool.c
3  *
4  * Version:  $Id$
5  *
6  * Copyright 2002  Globe.Net Communications Limited
7  */
8
9 #include "config.h"
10 #include <freeradius-devel/autoconf.h>
11 #include <freeradius-devel/libradius.h>
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <ctype.h>
17 #include <netinet/in.h>
18
19 #include <freeradius-devel/radiusd.h>
20 #include <freeradius-devel/modules.h>
21 #include <freeradius-devel/modpriv.h>
22
23 #include "rlm_sql.h"
24 #include "ip_set.h"
25
26 static const char rcsid[] = "$Id$";
27
28 /*
29  *      Define a structure for our module configuration.
30  */
31 typedef struct rlm_sqlippool_t {
32         char *sql_instance_name;
33
34         uint32_t range_start;
35         uint32_t range_stop;
36         uint32_t netmask;
37         int lease_duration;
38
39         SQL_INST *sql_inst;
40
41         char *pool_name;
42
43                                 /* Initialization sequence */
44         char *init_begin;       /* SQL query to begin */
45         char *init_query;       /* SQL query to select records */
46         char *init_delete;      /* SQL query to delete records */
47         char *init_insert;      /* SQL query to insert records */
48         char *init_commit;      /* SQL query to commit */
49         char *init_rollback;    /* SQL query to rollback */
50
51                                 /* Allocation sequence */
52         char *allocate_begin;   /* SQL query to begin */
53         char *allocate_clear;   /* SQL query to clear an IP */
54         char *allocate_find;    /* SQL query to find an unused IP */
55         char *allocate_update;  /* SQL query to mark an IP as used */
56         char *allocate_commit;  /* SQL query to commit */
57         char *allocate_rollback; /* SQL query to rollback */
58
59                                 /* Start sequence */
60         char *start_begin;      /* SQL query to begin */
61         char *start_update;     /* SQL query to update an IP entry */
62         char *start_commit;     /* SQL query to commit */
63         char *start_rollback;   /* SQL query to rollback */
64
65                                 /* Alive sequence */
66         char *alive_begin;      /* SQL query to begin */
67         char *alive_update;     /* SQL query to update an IP entry */
68         char *alive_commit;     /* SQL query to commit */
69         char *alive_rollback;   /* SQL query to rollback */
70
71                                 /* Stop sequence */
72         char *stop_begin;       /* SQL query to begin */
73         char *stop_clear;       /* SQL query to clear an IP */
74         char *stop_commit;      /* SQL query to commit */
75         char *stop_rollback;    /* SQL query to rollback */
76
77                                 /* On sequence */
78         char *on_begin;         /* SQL query to begin */
79         char *on_clear;         /* SQL query to clear an entire NAS */
80         char *on_commit;        /* SQL query to commit */
81         char *on_rollback;      /* SQL query to rollback */
82
83                                 /* Off sequence */
84         char *off_begin;        /* SQL query to begin */
85         char *off_clear;        /* SQL query to clear an entire NAS */
86         char *off_commit;       /* SQL query to commit */
87         char *off_rollback;     /* SQL query to rollback */
88 } rlm_sqlippool_t;
89
90 /*
91  *      A mapping of configuration file names to internal variables.
92  *
93  *      Note that the string is dynamically allocated, so it MUST
94  *      be freed.  When the configuration file parse re-reads the string,
95  *      it free's the old one, and strdup's the new one, placing the pointer
96  *      to the strdup'd string into 'config.string'.  This gets around
97  *      buffer over-flows.
98  */
99 static CONF_PARSER module_config[] = {
100   {"sql-instance-name",PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,sql_instance_name), NULL, "sql"},
101
102   { "range-start", PW_TYPE_IPADDR, offsetof(rlm_sqlippool_t,range_start), NULL, "0" },
103   { "range-stop", PW_TYPE_IPADDR, offsetof(rlm_sqlippool_t,range_stop), NULL, "0" },
104   { "netmask", PW_TYPE_IPADDR, offsetof(rlm_sqlippool_t,netmask), NULL, "0" },
105   { "lease-duration", PW_TYPE_INTEGER, offsetof(rlm_sqlippool_t,lease_duration), NULL, "86400"},
106
107   { "init-begin", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,init_begin), NULL, "BEGIN" },
108   { "init-query", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,init_query), NULL, "" },
109   { "init-delete", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,init_delete), NULL, "" },
110   { "init-insert", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,init_insert), NULL, "" },
111   { "init-commit", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,init_commit), NULL, "COMMIT" },
112   { "init-rollback", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,init_rollback), NULL, "ROLLBACK" },
113
114   { "allocate-begin", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,allocate_begin), NULL, "BEGIN" },
115   { "allocate-clear", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,allocate_clear), NULL, "" },
116   { "allocate-find", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,allocate_find), NULL, "" },
117   { "allocate-update", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,allocate_update), NULL, "" },
118   { "allocate-commit", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,allocate_commit), NULL, "COMMIT" },
119   { "allocate-rollback", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,allocate_rollback), NULL, "ROLLBACK" },
120
121   { "start-begin", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,start_begin), NULL, "BEGIN" },
122   { "start-update", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,start_update), NULL, "" },
123   { "start-commit", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,start_commit), NULL, "COMMIT" },
124   { "start-rollback", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,start_rollback), NULL, "ROLLBACK" },
125
126   { "alive-begin", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,alive_begin), NULL, "BEGIN" },
127   { "alive-update", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,alive_update), NULL, "" },
128   { "alive-commit", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,alive_commit), NULL, "COMMIT" },
129   { "alive-rollback", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,alive_rollback), NULL, "ROLLBACK" },
130
131   { "stop-begin", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,stop_begin), NULL, "BEGIN" },
132   { "stop-clear", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,stop_clear), NULL, "" },
133   { "stop-commit", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,stop_commit), NULL, "COMMIT" },
134   { "stop-rollback", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,stop_rollback), NULL, "ROLLBACK" },
135
136   { "on-begin", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,on_begin), NULL, "BEGIN" },
137   { "on-clear", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,on_clear), NULL, "" },
138   { "on-commit", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,on_commit), NULL, "COMMIT" },
139   { "on-rollback", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,on_rollback), NULL, "ROLLBACK" },
140
141   { "off-begin", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,off_begin), NULL, "BEGIN" },
142   { "off-clear", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,off_clear), NULL, "" },
143   { "off-commit", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,off_commit), NULL, "COMMIT" },
144   { "off-rollback", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,off_rollback), NULL, "ROLLBACK" },
145
146   { NULL, -1, 0, NULL, NULL }
147 };
148
149 /*
150  *      Replace %<whatever> in a string.
151  *
152  *      %P      pool_name
153  *      %I      param
154  *      %J      lease_duration
155  *
156  */
157 static int sqlippool_expand(char * out, int outlen, const char * fmt, void * instance, char * param, int param_len)
158 {
159         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
160         char *q;
161         const char *p;
162         char tmp[40]; /* For temporary storing of integers */
163         int openbraces;
164
165         openbraces = 0;
166         q = out;
167         for (p = fmt; *p ; p++) {
168                 int freespace;
169                 int c;
170
171                 /* Calculate freespace in output */
172                 freespace = outlen - (q - out);
173                 if (freespace <= 1)
174                         break;
175
176                 c = *p;
177                 if (c != '%' && c != '$' && c != '\\') {
178                         /*
179                          * We check if we're inside an open brace.  If we are
180                          * then we assume this brace is NOT literal, but is
181                          * a closing brace and apply it
182                          */
183                         if((c == '}') && openbraces) {
184                                 openbraces--;
185                                 continue;
186                         }
187                         *q++ = *p;
188                         continue;
189                 }
190
191                 if (*++p == '\0')
192                         break;
193
194                 if (c == '\\') {
195                         switch(*p) {
196                         case '\\':
197                                 *q++ = '\\';
198                                 break;
199                         case 't':
200                                 *q++ = '\t';
201                                 break;
202                         case 'n':
203                                 *q++ = '\n';
204                                 break;
205                         default:
206                                 *q++ = c;
207                                 *q++ = *p;
208                                 break;
209                         }
210                 }
211                 else if (c == '%') {
212                         switch(*p) {
213                         case '%':
214                                 *q++ = *p;
215                                 break;
216                         case 'P': /* pool name */
217                                 strNcpy(q, data->pool_name, freespace);
218                                 q += strlen(q);
219                                 break;
220                         case 'I': /* IP address */
221                                 if (param && param_len > 0) {
222                                         if (param_len > freespace) {
223                                                 strNcpy(q, param, freespace);
224                                                 q += strlen(q);
225                                         }
226                                         else {
227                                                 memcpy(q, param, param_len);
228                                                 q += param_len;
229                                         }
230                                 }
231                                 break;
232                         case 'J': /* lease duration */
233                                 sprintf(tmp, "%d", data->lease_duration);
234                                 strNcpy(q, tmp, freespace);
235                                 q += strlen(q);
236                                 break;
237                         default:
238                                 *q++ = '%';
239                                 *q++ = *p;
240                                 break;
241                         }
242                 }
243         }
244         *q = '\0';
245
246 #if 0
247         DEBUG2("sqlippool_expand: '%s'", out);
248 #endif
249
250         return strlen(out);
251 }
252
253 /*
254  * Query the database executing a command with no result rows
255  */
256 static int sqlippool_command(const char * fmt, SQLSOCK * sqlsocket, void * instance, REQUEST * request, char * param, int param_len)
257 {
258         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
259         char expansion[MAX_STRING_LEN * 4];
260         char query[MAX_STRING_LEN * 4];
261
262         sqlippool_expand(expansion, sizeof(expansion), fmt, instance, param, param_len);
263
264         /*
265          * Do an xlat on the provided string
266          */
267         if (request) {
268                 if (!radius_xlat(query, sizeof(query), expansion, request, NULL)) {
269                         radlog(L_ERR, "sqlippool_command: xlat failed.");
270                         return 0;
271                 }
272         }
273         else {
274                 strcpy(query, expansion);
275         }
276
277 #if 0
278         DEBUG2("sqlippool_command: '%s'", query);
279 #endif
280
281         if (rlm_sql_query(sqlsocket, data->sql_inst, query)){
282                 radlog(L_ERR, "sqlippool_command: database query error");
283                 return 0;
284         }
285
286         (data->sql_inst->module->sql_finish_query)(sqlsocket, data->sql_inst->config);
287
288         return 0;
289 }
290
291 /*
292  * Query the database expecting a single result row
293  */
294 static int sqlippool_query1(char * out, int outlen, const char * fmt, SQLSOCK * sqlsocket, void * instance, REQUEST * request, char * param, int param_len)
295 {
296         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
297         char expansion[MAX_STRING_LEN * 4];
298         char query[MAX_STRING_LEN * 4];
299         SQL_ROW row;
300         int r;
301
302         sqlippool_expand(expansion, sizeof(expansion), fmt, instance, param, param_len);
303
304         /*
305          * Do an xlat on the provided string
306          */
307         if (request) {
308                 if (!radius_xlat(query, sizeof(query), expansion, request, NULL)) {
309                         radlog(L_ERR, "sqlippool_command: xlat failed.");
310                         out[0] = '\0';
311                         return 0;
312                 }
313         }
314         else {
315                 strcpy(query, expansion);
316         }
317
318 #if 0
319         DEBUG2("sqlippool_query1: '%s'", query);
320 #endif
321
322         if (rlm_sql_select_query(sqlsocket, data->sql_inst, query)){
323                 radlog(L_ERR, "sqlippool_query1: database query error");
324                 out[0] = '\0';
325                 return 0;
326         }
327
328         r = rlm_sql_fetch_row(sqlsocket, data->sql_inst);
329         (data->sql_inst->module->sql_finish_select_query)(sqlsocket, data->sql_inst->config);
330
331         if (r) {
332                 DEBUG("sqlippool_query1: SQL query did not succeed");
333                 out[0] = '\0';
334                 return 0;
335         }
336
337         row = sqlsocket->row;
338         if (row == NULL) {
339                 DEBUG("sqlippool_query1: SQL query did not return any results");
340                 out[0] = '\0';
341                 return 0;
342         }
343
344         if (row[0] == NULL){
345                 DEBUG("sqlippool_query1: row[0] returned NULL");
346                 out[0] = '\0';
347                 return 0;
348         }
349
350         r = strlen(row[0]);
351         if (r >= outlen){
352                 DEBUG("sqlippool_query1: insufficient string space");
353                 out[0] = '\0';
354                 return 0;
355         }
356
357         strncpy(out, row[0], r);
358         out[r] = '\0';
359
360         return r;
361 }
362
363 /*
364  * Start the database query expecting multiple result rows
365  */
366 static int sqlippool_queryn(const char * fmt, SQLSOCK * sqlsocket, void * instance, REQUEST * request, char * param, int param_len)
367 {
368         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
369         char expansion[MAX_STRING_LEN * 4];
370         char query[MAX_STRING_LEN * 4];
371
372         sqlippool_expand(expansion, sizeof(expansion), fmt, instance, param, param_len);
373
374         /*
375          * Do an xlat on the provided string
376          */
377         if (request) {
378                 if (!radius_xlat(query, sizeof(query), expansion, request, NULL)) {
379                         radlog(L_ERR, "sqlippool_command: xlat failed.");
380                         return 0;
381                 }
382         }
383         else {
384                 strcpy(query, expansion);
385         }
386
387 #if 1
388         DEBUG2("sqlippool_queryn: '%s'", query);
389 #endif
390
391         if (rlm_sql_select_query(sqlsocket, data->sql_inst, query)){
392                 radlog(L_ERR, "sqlippool_query1: database query error");
393                 return 0;
394         }
395
396         return 1;
397 }
398
399 /*
400  * Fetch the next record from the multiple result set
401  */
402 static int sqlippool_queryn_fetch(char * out, int outlen, SQLSOCK * sqlsocket, void * instance)
403 {
404         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
405         SQL_ROW row;
406         int r;
407
408         r = rlm_sql_fetch_row(sqlsocket, data->sql_inst);
409
410         if (r) {
411                 DEBUG("sqlippool_queryn: SQL query did not succeed");
412                 out[0] = '\0';
413                 return 0;
414         }
415
416         row = sqlsocket->row;
417         if (row == NULL) {
418                 out[0] = '\0';
419                 return 0;
420         }
421
422         if (row[0] == NULL){
423                 DEBUG("sqlippool_queryn: row[0] returned NULL");
424                 out[0] = '\0';
425                 return 0;
426         }
427
428         r = strlen(row[0]);
429         if (r >= outlen){
430                 DEBUG("sqlippool_queryn: insufficient string space");
431                 out[0] = '\0';
432                 return 0;
433         }
434
435         strncpy(out, row[0], r);
436         out[r] = '\0';
437
438         return r;
439 }
440
441 /*
442  * Query the database expecting a multiple result rows
443  */
444 static int sqlippool_queryn_end(SQLSOCK * sqlsocket, void * instance)
445 {
446         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
447
448         (data->sql_inst->module->sql_finish_select_query)(sqlsocket, data->sql_inst->config);
449
450         return 1;
451 }
452
453 static int sqlippool_initialize_range(void * instance)
454 {
455         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
456         SQLSOCK * sqlsocket;
457         ip_set ips;
458         char ip_buffer[MAX_STRING_LEN];
459         int ip_buffer_len;
460
461         uint32_t h_start;       /* start in host order */
462         uint32_t h_stop;        /* stop in host order */
463         uint32_t h_netmask;     /* netmask in host order */
464
465         h_start = ntohl(data->range_start);
466         h_stop = ntohl(data->range_stop);
467         h_netmask = ntohl(data->netmask);
468
469         ip_set_initialize(&ips);
470
471         /*
472          * Now run the initialization sequence
473          */
474         sqlsocket = sql_get_socket(data->sql_inst);
475         if (sqlsocket == NULL) {
476                 DEBUG("rlm_sqlippool: cannot allocate sql connection for initialization sequence");
477                 return 0;
478         }
479
480         /*
481          * BEGIN
482          */
483         sqlippool_command(data->init_begin, sqlsocket, instance, NULL,
484                           (char *) NULL, 0);
485
486         /*
487          * QUERY
488          */
489         sqlippool_queryn(data->init_query, sqlsocket, instance, NULL,
490                          (char *) NULL, 0);
491
492         while (1)
493         {
494                 uint32_t ip;
495                 uint32_t h_ip;          /* ip in host order */
496                 lrad_ipaddr_t ipaddr;
497
498                 ip_buffer_len = sqlippool_queryn_fetch(ip_buffer, sizeof(ip_buffer),
499                                                        sqlsocket, instance);
500                 if (ip_buffer_len == 0)
501                         break;
502
503                 
504                 if (ip_hton(ip_buffer, AF_INET, &ipaddr) < 0) {
505                         radlog(L_ERR, "sqlippool_initialize_range: invalid IP number in pool");
506                         /* XXX store and delete */
507                         continue;
508                 }
509                 ip = ipaddr.ipaddr.ip4addr.s_addr;
510
511                 h_ip = ntohl(ip);
512                 ip_set_add(&ips, h_ip);
513         }
514
515         sqlippool_queryn_end(sqlsocket, data);
516
517         {
518                 int i;
519
520                 DEBUG(" len = %d, allocated = %d", ips.length, ips.allocated);
521                 for (i = 0; i < ips.length; i++) {
522                         DEBUG(" %d: %08x-%08x",
523                               i,
524                               ips.ranges[i].h_start,
525                               ips.ranges[i].h_finish);
526                 }
527         }
528
529         /*
530          * Loop over the rows and delete any entries are not in the pool
531          */
532         {
533                 int i;
534                 int h_ip;
535                 uint32_t or_result;
536
537                 for (i = 0; i < ips.length; i++) {
538                         for (h_ip = ips.ranges[i].h_start; h_ip <= ips.ranges[i].h_finish; h_ip++) {
539                                 if (h_ip < h_start)
540                                         ;
541                                 else if (h_ip > h_stop)
542                                         ;
543                                 else {
544                                         or_result = h_ip | h_netmask;
545                                         if (or_result == h_netmask || or_result == 0xffffffff)
546                                                 ;
547                                         else
548                                                 continue;
549                                 }
550
551                                 ip_ntoa(ip_buffer, htonl(h_ip));
552
553                                 /*
554                                  * DELETE
555                                  */
556                                 sqlippool_command(data->init_delete, sqlsocket, instance, NULL,
557                                                   ip_buffer, strlen(ip_buffer));
558                         }
559                 }
560         }
561
562         /*
563          * Loop over the range and insert any entries are not in the database
564          */
565         {
566                 uint32_t h_ip;          /* ip in host order */
567                 uint32_t or_result;
568
569                 h_start = ntohl(data->range_start);
570                 h_stop = ntohl(data->range_stop);
571                 h_netmask = ntohl(data->netmask);
572
573                 for (h_ip = h_start; h_ip <= h_stop; h_ip++) {
574                         /*
575                          * Network and broadcast addresses are excluded
576                          */
577                         or_result = h_ip | h_netmask;
578                         if (or_result == h_netmask || or_result == 0xffffffff) {
579                                 continue;
580                         }
581
582                         if (ip_set_test(&ips, h_ip))
583                                 continue;
584
585                         ip_ntoa(ip_buffer, htonl(h_ip));
586
587                         /*
588                          * INSERT
589                          */
590                         sqlippool_command(data->init_insert, sqlsocket, instance, NULL,
591                                           ip_buffer, strlen(ip_buffer));
592                 }
593         }
594
595         /*
596          * COMMIT
597          */
598         sqlippool_command(data->init_commit, sqlsocket, instance, NULL,
599                           (char *) NULL, 0);
600
601         sql_release_socket(data->sql_inst, sqlsocket);
602
603         ip_set_free(&ips);
604
605         return 1;
606 }
607
608 /*
609  *      Do any per-module initialization that is separate to each
610  *      configured instance of the module.  e.g. set up connections
611  *      to external databases, read configuration files, set up
612  *      dictionary entries, etc.
613  *
614  *      If configuration information is given in the config section
615  *      that must be referenced in later calls, store a handle to it
616  *      in *instance otherwise put a null pointer there.
617  */
618 static int sqlippool_instantiate(CONF_SECTION * conf, void ** instance)
619 {
620         rlm_sqlippool_t * data;
621         char * pool_name = NULL;
622
623         /*
624          *      Set up a storage area for instance data
625          */
626         data = rad_malloc(sizeof(*data));
627         memset(data, 0, sizeof(*data));
628
629         /*
630          *      If the configuration parameters can't be parsed, then
631          *      fail.
632          */
633         if (cf_section_parse(conf, data, module_config) < 0) {
634                 free(data);
635                 return -1;
636         }
637
638         if (data->sql_instance_name == NULL || strlen(data->sql_instance_name) == 0) {
639                 radlog(L_ERR, "rlm_sqlippool: the 'sql-instance-name' variable must be set.");
640                 free(data);
641                 exit(0);
642         }
643
644         /*
645          *      Check that all the queries are in place
646          */
647         if (data->init_query == NULL || strlen(data->init_query) == 0) {
648                 radlog(L_ERR, "rlm_sqlippool: the 'init-query' statement must be set.");
649                 free(data);
650                 exit(0);
651         }
652
653         if (data->init_delete == NULL || strlen(data->init_delete) == 0) {
654                 radlog(L_ERR, "rlm_sqlippool: the 'init-delete' statement must be set.");
655                 free(data);
656                 exit(0);
657         }
658
659         if (data->init_insert == NULL || strlen(data->init_insert) == 0) {
660                 radlog(L_ERR, "rlm_sqlippool: the 'init-insert' statement must be set.");
661                 free(data);
662                 exit(0);
663         }
664
665         if (data->allocate_clear == NULL || strlen(data->allocate_clear) == 0) {
666                 radlog(L_ERR, "rlm_sqlippool: the 'allocate-clear' statement must be set.");
667                 free(data);
668                 exit(0);
669         }
670
671         if (data->allocate_find == NULL || strlen(data->allocate_find) == 0) {
672                 radlog(L_ERR, "rlm_sqlippool: the 'allocate_find' statement must be set.");
673                 free(data);
674                 exit(0);
675         }
676
677         if (data->allocate_update == NULL || strlen(data->allocate_update) == 0) {
678                 radlog(L_ERR, "rlm_sqlippool: the 'allocate_update' statement must be set.");
679                 free(data);
680                 exit(0);
681         }
682
683         if (data->start_update == NULL || strlen(data->start_update) == 0) {
684                 radlog(L_ERR, "rlm_sqlippool: the 'start-update' statement must be set.");
685                 free(data);
686                 exit(0);
687         }
688
689         if (data->alive_update == NULL || strlen(data->alive_update) == 0) {
690                 radlog(L_ERR, "rlm_sqlippool: the 'alive-update' statement must be set.");
691                 free(data);
692                 exit(0);
693         }
694
695         if (data->stop_clear == NULL || strlen(data->stop_clear) == 0) {
696                 radlog(L_ERR, "rlm_sqlippool: the 'stop-clear' statement must be set.");
697                 free(data);
698                 exit(0);
699         }
700
701         if (data->on_clear == NULL || strlen(data->on_clear) == 0) {
702                 radlog(L_ERR, "rlm_sqlippool: the 'on-clear' statement must be set.");
703                 free(data);
704                 exit(0);
705         }
706
707         if (data->off_clear == NULL || strlen(data->off_clear) == 0) {
708                 radlog(L_ERR, "rlm_sqlippool: the 'off-clear' statement must be set.");
709                 free(data);
710                 exit(0);
711         }
712
713         pool_name = cf_section_name2(conf);
714         if (pool_name != NULL)
715                 data->pool_name = strdup(pool_name);
716         else
717                 data->pool_name = strdup("ippool");
718
719         data->sql_inst = (SQL_INST *)(find_module_instance(cf_section_find("modules"), data->sql_instance_name))->insthandle;
720         if (data->sql_inst == NULL) {
721                 radlog(L_ERR, "sqlippool_instantiate: failed to find sql instance named %s", data->sql_instance_name);
722                 free(data);
723                 exit(0);
724         }
725
726         sqlippool_initialize_range(data);
727
728         *instance = data;
729         return 0;
730 }
731
732 /*
733  *      Allocate an IP number from the pool.
734  */
735 static int sqlippool_postauth(void *instance, REQUEST * request)
736 {
737         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
738         char allocation[MAX_STRING_LEN];
739         int allocation_len;
740         lrad_ipaddr_t ipaddr;
741         uint32_t ip_allocation;
742         VALUE_PAIR * vp;
743         SQLSOCK * sqlsocket;
744
745         /*
746          * If there is a Framed-IP-Address attribute in the reply do nothing
747          */
748         if (pairfind(request->reply->vps, PW_FRAMED_IP_ADDRESS) != NULL) {
749                 DEBUG("rlm_sqlippool: Framed-IP-Address already exists");
750                 return RLM_MODULE_NOOP;
751         }
752
753         /*
754          * Check if Pool-Name attribute exists. If it exists check our name and
755          * run only if they match
756          */
757         if ((vp = pairfind(request->config_items, PW_POOL_NAME)) != NULL) {
758                 if (data->pool_name == NULL || strcmp(data->pool_name, vp->vp_strvalue) != 0) {
759                         DEBUG("rlm_sqlippool: pool_name does not match");
760                         return RLM_MODULE_NOOP;
761                 }
762         }
763         else {
764                 DEBUG("rlm_sqlippool: missing pool_name");
765                 return RLM_MODULE_NOOP;
766         }
767
768         if (pairfind(request->packet->vps, PW_NAS_IP_ADDRESS) == NULL) {
769                 DEBUG("rlm_sqlippool: unknown NAS-IP-Address");
770                 return RLM_MODULE_NOOP;
771         }
772
773         if (pairfind(request->packet->vps, PW_NAS_PORT) == NULL) {
774                 DEBUG("rlm_sqlippool: unknown NAS-Port");
775                 return RLM_MODULE_NOOP;
776         }
777
778         sqlsocket = sql_get_socket(data->sql_inst);
779         if (sqlsocket == NULL) {
780                 DEBUG("rlm_sqlippool: cannot allocate sql connection");
781                 return RLM_MODULE_NOOP;
782         }
783
784         /*
785          * BEGIN
786          */
787         sqlippool_command(data->allocate_begin, sqlsocket, instance, request,
788                           (char *) NULL, 0);
789
790         /*
791          * CLEAR
792          */
793         sqlippool_command(data->allocate_clear, sqlsocket, instance, request,
794                           (char *) NULL, 0);
795
796         /*
797          * FIND
798          */
799         allocation_len = sqlippool_query1(allocation, sizeof(allocation),
800                                           data->allocate_find, sqlsocket, instance, request,
801                                           (char *) NULL, 0);
802         DEBUG("rlm_sqlippool: ip=[%s] len=%d", allocation, allocation_len);
803
804         if (allocation_len == 0)
805         {
806                 /*
807                  * COMMIT
808                  */
809                 sqlippool_command(data->allocate_commit, sqlsocket, instance, request,
810                                   (char *) NULL, 0);
811
812                 DEBUG("rlm_sqlippool: IP number could not be allocated.");
813                 sql_release_socket(data->sql_inst, sqlsocket);
814                 return RLM_MODULE_NOOP;
815         }
816
817         if (ip_hton(allocation, AF_INET, &ipaddr) < 0)
818         {
819                 /*
820                  * Invalid IP number - run INIT-DELETE and complain
821                  */
822
823                 /*
824                  * INIT_DELETE
825                  */
826                 sqlippool_command(data->init_delete, sqlsocket, instance, NULL,
827                                   allocation, allocation_len);
828
829                 /*
830                  * COMMIT
831                  */
832                 sqlippool_command(data->allocate_commit, sqlsocket, instance, request,
833                                   (char *) NULL, 0);
834
835                 DEBUG("rlm_sqlippool: Invalid IP number [%s] returned from database query.", allocation);
836                 sql_release_socket(data->sql_inst, sqlsocket);
837                 return RLM_MODULE_NOOP;
838         }
839         ip_allocation = ipaddr.ipaddr.ip4addr.s_addr;
840
841         /*
842          * UPDATE
843          */
844         sqlippool_command(data->allocate_update, sqlsocket, instance, request,
845                           allocation, allocation_len);
846
847         DEBUG("rlm_sqlippool: Allocated IP %s [%08x]", allocation, ip_allocation);
848
849         if ((vp = paircreate(PW_FRAMED_IP_ADDRESS, PW_TYPE_IPADDR)) == NULL) {
850                 radlog(L_ERR|L_CONS, "no memory");
851                 sql_release_socket(data->sql_inst, sqlsocket);
852                 return RLM_MODULE_NOOP;
853         }
854         vp->lvalue = ip_allocation;
855         pairadd(&request->reply->vps, vp);
856
857         /*
858          * COMMIT
859          */
860         sqlippool_command(data->allocate_commit, sqlsocket, instance, request,
861                           (char *) NULL, 0);
862
863         sql_release_socket(data->sql_inst, sqlsocket);
864         return RLM_MODULE_OK;
865 }
866
867 static int sqlippool_accounting_start(void * instance, REQUEST * request)
868 {
869         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
870         SQLSOCK * sqlsocket;
871
872         if (pairfind(request->packet->vps, PW_NAS_PORT) == NULL) {
873                 DEBUG("rlm_ippool: Could not find port number in packet.");
874                 return RLM_MODULE_NOOP;
875         }
876
877         if (pairfind(request->packet->vps, PW_NAS_IP_ADDRESS) == NULL) {
878                 DEBUG("rlm_ippool: Could not find nas information in packet.");
879                 return RLM_MODULE_NOOP;
880         }
881
882         sqlsocket = sql_get_socket(data->sql_inst);
883         if (sqlsocket == NULL) {
884                 DEBUG("rlm_sqlippool: cannot allocate sql connection");
885                 return RLM_MODULE_NOOP;
886         }
887
888         /*
889          * BEGIN
890          */
891         sqlippool_command(data->start_begin, sqlsocket, instance, request,
892                           (char *) NULL, 0);
893
894         /*
895          * UPDATE
896          */
897         sqlippool_command(data->start_update, sqlsocket, instance, request,
898                           (char *) NULL, 0);
899
900         /*
901          * COMMIT
902          */
903         sqlippool_command(data->start_commit, sqlsocket, instance, request,
904                           (char *) NULL, 0);
905
906         sql_release_socket(data->sql_inst, sqlsocket);
907
908         return RLM_MODULE_OK;
909 }
910
911 static int sqlippool_accounting_alive(void * instance, REQUEST * request)
912 {
913         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
914         SQLSOCK * sqlsocket;
915
916         if (pairfind(request->packet->vps, PW_NAS_PORT) == NULL) {
917                 DEBUG("rlm_ippool: Could not find port number in packet.");
918                 return RLM_MODULE_NOOP;
919         }
920
921         if (pairfind(request->packet->vps, PW_NAS_IP_ADDRESS) == NULL) {
922                 DEBUG("rlm_ippool: Could not find nas information in packet.");
923                 return RLM_MODULE_NOOP;
924         }
925
926         sqlsocket = sql_get_socket(data->sql_inst);
927         if (sqlsocket == NULL) {
928                 DEBUG("rlm_sqlippool: cannot allocate sql connection");
929                 return RLM_MODULE_NOOP;
930         }
931
932         /*
933          * BEGIN
934          */
935         sqlippool_command(data->alive_begin, sqlsocket, instance, request,
936                           (char *) NULL, 0);
937
938         /*
939          * UPDATE
940          */
941         sqlippool_command(data->alive_update, sqlsocket, instance, request,
942                           (char *) NULL, 0);
943
944         /*
945          * COMMIT
946          */
947         sqlippool_command(data->alive_commit, sqlsocket, instance, request,
948                           (char *) NULL, 0);
949
950         sql_release_socket(data->sql_inst, sqlsocket);
951
952         return RLM_MODULE_OK;
953 }
954
955 static int sqlippool_accounting_stop(void * instance, REQUEST * request)
956 {
957         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
958         SQLSOCK * sqlsocket;
959
960         if (pairfind(request->packet->vps, PW_NAS_PORT) == NULL) {
961                 DEBUG("rlm_ippool: Could not find port number in packet.");
962                 return RLM_MODULE_NOOP;
963         }
964
965         if (pairfind(request->packet->vps, PW_NAS_IP_ADDRESS) == NULL) {
966                 DEBUG("rlm_ippool: Could not find nas information in packet.");
967                 return RLM_MODULE_NOOP;
968         }
969
970         sqlsocket = sql_get_socket(data->sql_inst);
971         if (sqlsocket == NULL) {
972                 DEBUG("rlm_sqlippool: cannot allocate sql connection");
973                 return RLM_MODULE_NOOP;
974         }
975
976         /*
977          * BEGIN
978          */
979         sqlippool_command(data->stop_begin, sqlsocket, instance, request,
980                           (char *) NULL, 0);
981
982         /*
983          * CLEAR
984          */
985         sqlippool_command(data->stop_clear, sqlsocket, instance, request,
986                           (char *) NULL, 0);
987
988         /*
989          * COMMIT
990          */
991         sqlippool_command(data->stop_commit, sqlsocket, instance, request,
992                           (char *) NULL, 0);
993
994         sql_release_socket(data->sql_inst, sqlsocket);
995
996         return RLM_MODULE_OK;
997 }
998
999 static int sqlippool_accounting_on(void * instance, REQUEST * request)
1000 {
1001         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
1002         SQLSOCK * sqlsocket;
1003
1004         if (pairfind(request->packet->vps, PW_NAS_IP_ADDRESS) == NULL) {
1005                 DEBUG("rlm_ippool: Could not find nas information in packet.");
1006                 return RLM_MODULE_NOOP;
1007         }
1008
1009         sqlsocket = sql_get_socket(data->sql_inst);
1010         if (sqlsocket == NULL) {
1011                 DEBUG("rlm_sqlippool: cannot allocate sql connection");
1012                 return RLM_MODULE_NOOP;
1013         }
1014
1015         /*
1016          * BEGIN
1017          */
1018         sqlippool_command(data->on_begin, sqlsocket, instance, request,
1019                           (char *) NULL, 0);
1020
1021         /*
1022          * CLEAR
1023          */
1024         sqlippool_command(data->on_clear, sqlsocket, instance, request,
1025                           (char *) NULL, 0);
1026
1027         /*
1028          * COMMIT
1029          */
1030         sqlippool_command(data->on_commit, sqlsocket, instance, request,
1031                           (char *) NULL, 0);
1032
1033         sql_release_socket(data->sql_inst, sqlsocket);
1034
1035         return RLM_MODULE_OK;
1036 }
1037
1038 static int sqlippool_accounting_off(void * instance, REQUEST * request)
1039 {
1040         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
1041         SQLSOCK * sqlsocket;
1042
1043         if (pairfind(request->packet->vps, PW_NAS_IP_ADDRESS) == NULL) {
1044                 DEBUG("rlm_ippool: Could not find nas information in packet.");
1045                 return RLM_MODULE_NOOP;
1046         }
1047
1048         sqlsocket = sql_get_socket(data->sql_inst);
1049         if (sqlsocket == NULL) {
1050                 DEBUG("rlm_sqlippool: cannot allocate sql connection");
1051                 return RLM_MODULE_NOOP;
1052         }
1053
1054         /*
1055          * BEGIN
1056          */
1057         sqlippool_command(data->off_begin, sqlsocket, instance, request,
1058                           (char *) NULL, 0);
1059
1060         /*
1061          * CLEAR
1062          */
1063         sqlippool_command(data->off_clear, sqlsocket, instance, request,
1064                           (char *) NULL, 0);
1065
1066         /*
1067          * COMMIT
1068          */
1069         sqlippool_command(data->off_commit, sqlsocket, instance, request,
1070                           (char *) NULL, 0);
1071
1072         sql_release_socket(data->sql_inst, sqlsocket);
1073
1074         return RLM_MODULE_OK;
1075 }
1076
1077 /*
1078  *      Check for an Accounting-Stop
1079  *      If we find one and we have allocated an IP to this nas/port combination, deallocate it.
1080  */
1081 static int sqlippool_accounting(void * instance, REQUEST * request)
1082 {
1083         VALUE_PAIR * vp;
1084         int acct_status_type;
1085
1086         vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE);
1087         if (vp == NULL) {
1088                 DEBUG("rlm_sqlippool: Could not find account status type in packet.");
1089                 return RLM_MODULE_NOOP;
1090         }
1091         acct_status_type = vp->lvalue;
1092
1093         switch (acct_status_type) {
1094         case PW_STATUS_START:
1095                 return sqlippool_accounting_start(instance, request);
1096
1097         case PW_STATUS_ALIVE:
1098                 return sqlippool_accounting_alive(instance, request);
1099
1100         case PW_STATUS_STOP:
1101                 return sqlippool_accounting_stop(instance, request);
1102
1103         case PW_STATUS_ACCOUNTING_ON:
1104                 return sqlippool_accounting_on(instance, request);
1105
1106         case PW_STATUS_ACCOUNTING_OFF:
1107                 return sqlippool_accounting_off(instance, request);
1108
1109         default:
1110                 /* We don't care about any other accounting packet */
1111                 return RLM_MODULE_NOOP;
1112         }
1113 }
1114
1115 static int sqlippool_detach(void *instance)
1116 {
1117         rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance;
1118
1119         free(data->sql_instance_name);
1120         free(data->pool_name);
1121
1122         free(data->init_begin);
1123         free(data->init_query);
1124         free(data->init_delete);
1125         free(data->init_insert);
1126         free(data->init_commit);
1127         free(data->init_rollback);
1128
1129         free(data->allocate_begin);
1130         free(data->allocate_clear);
1131         free(data->allocate_find);
1132         free(data->allocate_update);
1133         free(data->allocate_commit);
1134         free(data->allocate_rollback);
1135
1136         free(data->start_begin);
1137         free(data->start_update);
1138         free(data->start_commit);
1139         free(data->start_rollback);
1140
1141         free(data->alive_begin);
1142         free(data->alive_update);
1143         free(data->alive_commit);
1144         free(data->alive_rollback);
1145
1146         free(data->stop_begin);
1147         free(data->stop_clear);
1148         free(data->stop_commit);
1149         free(data->stop_rollback);
1150
1151         free(data->on_begin);
1152         free(data->on_clear);
1153         free(data->on_commit);
1154         free(data->on_rollback);
1155
1156         free(data->off_begin);
1157         free(data->off_clear);
1158         free(data->off_commit);
1159         free(data->off_rollback);
1160
1161         return 0;
1162 }
1163
1164 /*
1165  *      The module name should be the only globally exported symbol.
1166  *      That is, everything else should be 'static'.
1167  *
1168  *      If the module needs to temporarily modify it's instantiation
1169  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
1170  *      The server will then take care of ensuring that the module
1171  *      is single-threaded.
1172  */
1173 module_t rlm_sqlippool = {
1174         RLM_MODULE_INIT,
1175         "sqlippool",
1176         RLM_TYPE_THREAD_SAFE,           /* type */
1177         sqlippool_instantiate,          /* instantiation */
1178         sqlippool_detach,               /* detach */
1179         {
1180                 NULL,                   /* authentication */
1181                 NULL,                   /* authorization */
1182                 NULL,                   /* preaccounting */
1183                 sqlippool_accounting,   /* accounting */
1184                 NULL,                   /* checksimul */
1185                 NULL,                   /* pre-proxy */
1186                 NULL,                   /* post-proxy */
1187                 sqlippool_postauth      /* post-auth */
1188         }
1189 };