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