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